diff --git a/enhanced_exi_viewer.c b/enhanced_exi_viewer.c index aee3976..fdf3ae4 100644 --- a/enhanced_exi_viewer.c +++ b/enhanced_exi_viewer.c @@ -17,6 +17,205 @@ #define BUFFER_SIZE 4096 +// Network protocol patterns and definitions +#define ETH_TYPE_IPV6 0x86DD // Ethernet Type: IPv6 +#define IPV6_NEXT_HEADER_TCP 0x06 // IPv6 Next Header: TCP +#define TCP_V2G_PORT 15118 // V2G communication port + +// V2G Transfer Protocol patterns and definitions +#define V2G_PROTOCOL_VERSION 0x01 // Protocol Version +#define V2G_INV_PROTOCOL_VERSION 0xFE // Inverse Protocol Version +#define V2G_PAYLOAD_ISO_DIN_SAP 0x8001 // ISO 15118-2/DIN/SAP payload type +#define V2G_PAYLOAD_ISO2 0x8002 // ISO 15118-20 payload type +#define EXI_START_PATTERN 0x8098 // EXI document start pattern + +// Function to detect and extract EXI body from V2G Transfer Protocol data +size_t extract_exi_body(uint8_t* input_data, size_t input_size, uint8_t* output_data, size_t output_size) { + if (input_size < 8) { + // Too small for V2G TP header, assume it's pure EXI + if (input_size <= output_size) { + memcpy(output_data, input_data, input_size); + return input_size; + } + return 0; + } + + // Check for V2G Transfer Protocol header + if (input_data[0] == V2G_PROTOCOL_VERSION && input_data[1] == V2G_INV_PROTOCOL_VERSION) { + uint16_t payload_type = (input_data[2] << 8) | input_data[3]; + + if (payload_type == V2G_PAYLOAD_ISO_DIN_SAP || payload_type == V2G_PAYLOAD_ISO2) { + // Valid V2G TP header detected: skip 8-byte header + size_t exi_size = input_size - 8; + if (exi_size <= output_size) { + memcpy(output_data, input_data + 8, exi_size); + return exi_size; + } + return 0; + } + } + + // Look for EXI start pattern anywhere in the data + for (size_t i = 0; i <= input_size - 2; i++) { + uint16_t pattern = (input_data[i] << 8) | input_data[i + 1]; + if (pattern == EXI_START_PATTERN) { + // Found EXI start pattern + size_t exi_size = input_size - i; + if (exi_size <= output_size) { + memcpy(output_data, input_data + i, exi_size); + return exi_size; + } + return 0; + } + } + + // No pattern found, assume it's pure EXI + if (input_size <= output_size) { + memcpy(output_data, input_data, input_size); + return input_size; + } + + return 0; +} + +// Function to get payload type name +const char* get_payload_type_name(uint16_t payload_type) { + switch(payload_type) { + case V2G_PAYLOAD_ISO_DIN_SAP: return "ISO 15118-2/DIN/SAP"; + case V2G_PAYLOAD_ISO2: return "ISO 15118-20"; + default: return "Unknown"; + } +} + +// Function to analyze complete packet structure +void analyze_data_structure(uint8_t* data, size_t size) { + printf("=== Data Structure Analysis ===\n"); + printf("Total size: %zu bytes\n", size); + + size_t offset = 0; + + // Check for Ethernet header (at least 14 bytes) + if (size >= 14) { + uint16_t eth_type = (data[12] << 8) | data[13]; + if (eth_type == ETH_TYPE_IPV6) { + printf("Layer 2: Ethernet Frame\n"); + printf(" Destination MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + data[0], data[1], data[2], data[3], data[4], data[5]); + printf(" Source MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + data[6], data[7], data[8], data[9], data[10], data[11]); + printf(" EtherType: 0x%04x (IPv6)\n", eth_type); + offset = 14; + + // Check for IPv6 header (40 bytes) + if (size >= offset + 40) { + uint8_t version = (data[offset] >> 4) & 0x0F; + uint16_t payload_length = (data[offset + 4] << 8) | data[offset + 5]; + uint8_t next_header = data[offset + 6]; + uint8_t hop_limit = data[offset + 7]; + + if (version == 6) { + printf("Layer 3: IPv6 Header\n"); + printf(" Version: %d\n", version); + printf(" Payload Length: %u\n", payload_length); + printf(" Next Header: %u", next_header); + if (next_header == IPV6_NEXT_HEADER_TCP) { + printf(" (TCP)\n"); + } else { + printf("\n"); + } + printf(" Hop Limit: %u\n", hop_limit); + + // Show IPv6 addresses + printf(" Source Address: "); + for (int i = 0; i < 16; i += 2) { + printf("%02x%02x", data[offset + 8 + i], data[offset + 8 + i + 1]); + if (i < 14) printf(":"); + } + printf("\n Destination Address: "); + for (int i = 0; i < 16; i += 2) { + printf("%02x%02x", data[offset + 24 + i], data[offset + 24 + i + 1]); + if (i < 14) printf(":"); + } + printf("\n"); + + offset += 40; + + // Check for TCP header (at least 20 bytes) + if (next_header == IPV6_NEXT_HEADER_TCP && size >= offset + 20) { + uint16_t src_port = (data[offset] << 8) | data[offset + 1]; + uint16_t dst_port = (data[offset + 2] << 8) | data[offset + 3]; + uint32_t seq_num = (data[offset + 4] << 24) | (data[offset + 5] << 16) | + (data[offset + 6] << 8) | data[offset + 7]; + uint8_t tcp_header_len = (data[offset + 12] >> 4) * 4; + + printf("Layer 4: TCP Header\n"); + printf(" Source Port: %u\n", src_port); + printf(" Destination Port: %u", dst_port); + if (dst_port == TCP_V2G_PORT || src_port == TCP_V2G_PORT) { + printf(" (V2G Communication)\n"); + } else { + printf("\n"); + } + printf(" Sequence Number: %u\n", seq_num); + printf(" TCP Header Length: %u bytes\n", tcp_header_len); + + offset += tcp_header_len; + } + } + } + } + } + + // Look for V2G Transfer Protocol starting from current offset + if (size >= offset + 8 && + data[offset] == V2G_PROTOCOL_VERSION && + data[offset + 1] == V2G_INV_PROTOCOL_VERSION) { + + printf("Layer 7: V2G Transfer Protocol\n"); + printf(" Protocol Version: 0x%02x\n", data[offset]); + printf(" Inverse Protocol Version: 0x%02x\n", data[offset + 1]); + + uint16_t payload_type = (data[offset + 2] << 8) | data[offset + 3]; + printf(" Payload Type: 0x%04x (%s)\n", payload_type, get_payload_type_name(payload_type)); + + uint32_t payload_length = (data[offset + 4] << 24) | (data[offset + 5] << 16) | + (data[offset + 6] << 8) | data[offset + 7]; + printf(" Payload Length: %u\n", payload_length); + printf("EXI body starts at offset: %zu\n", offset + 8); + + // Verify payload length + if (size >= offset + 8 && (size - offset - 8) == payload_length) { + printf("✓ Payload length matches actual data (%zu bytes)\n", size - offset - 8); + } else if (size >= offset + 8) { + printf("⚠ Payload length mismatch: expected %u, got %zu bytes\n", + payload_length, size - offset - 8); + } + + offset += 8; + } + else if (offset == 0) { + // No network headers detected, check if it's direct V2G TP or EXI + if (size >= 4) { + uint32_t header = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + printf("First 4 bytes: 0x%08X\n", header); + printf("Protocol: Unknown or Direct EXI\n"); + } + } + + // Look for EXI pattern + for (size_t i = 0; i <= size - 2; i++) { + uint16_t pattern = (data[i] << 8) | data[i + 1]; + if (pattern == EXI_START_PATTERN) { + printf("EXI start pattern (0x8098) found at offset: %zu\n", i); + if (i >= offset) { + printf("EXI payload size: %zu bytes\n", size - i); + } + break; + } + } + printf("\n"); +} + // Helper function to convert char* string to exi_string_character_t* array static int writeStringToEXIString(char* string, exi_string_character_t* exiString) { int pos = 0; @@ -385,6 +584,86 @@ int parse_xml_to_iso1(const char* xml_content, struct iso1EXIDocument* doc) { return 0; } + // Check for CurrentDemandRes + if (strstr(xml_content, "") || strstr(xml_content, "")) { + doc->V2G_Message.Body.CurrentDemandRes_isUsed = 1; + init_iso1CurrentDemandResType(&doc->V2G_Message.Body.CurrentDemandRes); + + // Parse ResponseCode + char* response_code = find_tag_content(xml_content, "ResponseCode"); + if (response_code) { + doc->V2G_Message.Body.CurrentDemandRes.ResponseCode = atoi(response_code); + } + + // Parse DC_EVSEStatus + char* evse_isolation = find_tag_content_ns(xml_content, "EVSEIsolationStatus"); + if (evse_isolation) { + doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus = atoi(evse_isolation); + doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus_isUsed = 1u; + } + + char* evse_status = find_tag_content_ns(xml_content, "EVSEStatusCode"); + if (evse_status) { + doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEStatusCode = atoi(evse_status); + } + + // Parse EVSEPresentVoltage using bounded section approach + char* voltage_section = strstr(xml_content, ""); + if (!voltage_section) voltage_section = strstr(xml_content, ""); + if (voltage_section) { + char* voltage_end = strstr(voltage_section, ""); + if (!voltage_end) voltage_end = strstr(voltage_section, ""); + if (voltage_end) { + parse_physical_value_from_section(voltage_section, voltage_end, &doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage); + } + } + + // Parse EVSEPresentCurrent using bounded section approach + char* current_section = strstr(xml_content, ""); + if (!current_section) current_section = strstr(xml_content, ""); + if (current_section) { + char* current_end = strstr(current_section, ""); + if (!current_end) current_end = strstr(current_section, ""); + if (current_end) { + parse_physical_value_from_section(current_section, current_end, &doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent); + } + } + + // Parse limit achieved flags + char* current_limit = find_tag_content(xml_content, "EVSECurrentLimitAchieved"); + if (current_limit) { + doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved = (strcmp(current_limit, "true") == 0); + } + + char* voltage_limit = find_tag_content(xml_content, "EVSEVoltageLimitAchieved"); + if (voltage_limit) { + doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved = (strcmp(voltage_limit, "true") == 0); + } + + char* power_limit = find_tag_content(xml_content, "EVSEPowerLimitAchieved"); + if (power_limit) { + doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved = (strcmp(power_limit, "true") == 0); + } + + // Parse EVSEID + char* evseid = find_tag_content(xml_content, "EVSEID"); + if (evseid) { + size_t len = strlen(evseid); + if (len < sizeof(doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters)) { + memcpy(doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters, evseid, len); + doc->V2G_Message.Body.CurrentDemandRes.EVSEID.charactersLen = len; + } + } + + // Parse SAScheduleTupleID + char* sa_schedule = find_tag_content(xml_content, "SAScheduleTupleID"); + if (sa_schedule) { + doc->V2G_Message.Body.CurrentDemandRes.SAScheduleTupleID = atoi(sa_schedule); + } + + return 0; + } + return -1; // Unsupported message type } @@ -758,6 +1037,28 @@ int main(int argc, char *argv[]) { } if (pos > 32) printf("..."); printf("\\n\\n"); + + // Analyze data structure and extract EXI body + analyze_data_structure(buffer, pos); + } + + // Extract EXI body from V2G Transfer Protocol data + uint8_t exi_buffer[BUFFER_SIZE]; + size_t exi_size = extract_exi_body(buffer, pos, exi_buffer, BUFFER_SIZE); + + if (exi_size != pos) { + if (!xml_mode) { + printf("EXI body extracted: %zu bytes (was %zu bytes)\\n", exi_size, pos); + printf("EXI hex data: "); + for(size_t i = 0; i < (exi_size > 32 ? 32 : exi_size); i++) { + printf("%02X ", exi_buffer[i]); + } + if (exi_size > 32) printf("..."); + printf("\\n\\n"); + } + // Use extracted EXI data + memcpy(buffer, exi_buffer, exi_size); + pos = exi_size; } // Setup stream