/****************************************************************************** * Copyright (c) 2000-2019 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * * Contributors: * Endre Kulcsar - initial implementation and initial documentation ******************************************************************************/ // // File: TCP_EncDec.cc // Rev: R3A // Prodnr: CNL 113 675 #include "TCP_Types.hh" /* ================= Common defines ================= */ /* Logs the name of the function on entering */ #define Log_function_name_on_enter() \ TTCN_logger.log(TTCN_DEBUG, "Entering %s", __FUNCTION__); /* Logs the name of the function on leaving */ #define Log_function_name_on_leave() \ TTCN_logger.log(TTCN_DEBUG, "Leaving %s", __FUNCTION__); #define Log_object(o) \ TTCN_logger.begin_event(TTCN_DEBUG); \ (o).log(); \ TTCN_logger.end_event(); \ #define Get_MSB(val) (((val) & 0xff00) >> 8) #define Get_LSB(val) ((val) & 0x00ff) namespace TCP__Types { void Calculate_cksum(const unsigned char *ptr, int datalen, unsigned char * pl_checksum) { Log_function_name_on_enter(); unsigned long sum = 0; for (int i = 0; i <= datalen - 2; i = i + 2) sum += (ptr[i + 1] << 8) + ptr[i]; if (datalen % 2) // datalen is odd { sum += ptr[datalen - 1]; } sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16); sum = ~sum; pl_checksum[0] = Get_LSB(sum); pl_checksum[1] = Get_MSB(sum); Log_function_name_on_leave(); return; } void calc_TCP_checksum_IPv4( const unsigned char * pl__ip__source, const unsigned char * pl__ip__dest, const unsigned char * pl__tcp__segment, unsigned int pl_tcp_segment_length, unsigned char * pl__checksum ) { Log_function_name_on_enter(); unsigned char tcpBuf[65536]; unsigned char zero = 0x00; unsigned char six = 0x06; unsigned char length_u =(pl_tcp_segment_length & 0xff00 ) >> 8; unsigned char length_l =(pl_tcp_segment_length & 0xff ); //construct pseudo header memcpy(tcpBuf,pl__ip__source,4); memcpy(tcpBuf+4,pl__ip__dest,4); memcpy(tcpBuf+8,&zero,1); memcpy(tcpBuf+9,&six,1); memcpy(tcpBuf+10,&length_u,1); memcpy(tcpBuf+11,&length_l,1); memcpy(tcpBuf+12,pl__tcp__segment,pl_tcp_segment_length); Calculate_cksum(tcpBuf, pl_tcp_segment_length+12,pl__checksum); Log_function_name_on_leave(); return; } void calc_TCP_checksum_IPv6( const unsigned char * pl__ip__source, const unsigned char * pl__ip__dest, const unsigned char * pl__tcp__segment, unsigned int pl_tcp_segment_length, unsigned char * pl__checksum ) { Log_function_name_on_enter(); unsigned char tcpBuf[65536]; unsigned char zero = 0x00; unsigned char six = 0x06; unsigned char length_1 =(pl_tcp_segment_length & 0xff000000 ) >> 24; unsigned char length_2 =(pl_tcp_segment_length & 0xff0000 ) >> 16; unsigned char length_3 =(pl_tcp_segment_length & 0xff00 ) >> 8; unsigned char length_4 =(pl_tcp_segment_length & 0xff ); //construct pseudo header memcpy(tcpBuf,pl__ip__source,16); memcpy(tcpBuf+16,pl__ip__dest,16); memcpy(tcpBuf+32,&length_1,1); memcpy(tcpBuf+33,&length_2,1); memcpy(tcpBuf+34,&length_3,1); memcpy(tcpBuf+35,&length_4,1); memcpy(tcpBuf+36,&zero,1); memcpy(tcpBuf+37,&zero,1); memcpy(tcpBuf+38,&zero,1); memcpy(tcpBuf+39,&six,1); memcpy(tcpBuf+40,pl__tcp__segment,pl_tcp_segment_length); Calculate_cksum(tcpBuf, pl_tcp_segment_length+40,pl__checksum); Log_function_name_on_leave(); return; } OCTETSTRING f__enc__PDU__TCP( const OCTETSTRING& pl__ip__source, const OCTETSTRING& pl__ip__dest, const PDU__TCP& pdu, const BOOLEAN& pl__autoDataOffset, const BOOLEAN& pl__autoChecksum ) { Log_function_name_on_enter(); if (TTCN_Logger::log_this_event(TTCN_DEBUG)) { TTCN_Logger::begin_event(TTCN_DEBUG); TTCN_Logger::log_event("Encoding PDU_TCP: "); pdu.log(); TTCN_Logger::end_event(); } TTCN_Buffer bb; // make local copy of PDU PDU__TCP pdu2 = pdu; // Automatically add padding to options field if length of options is not divisible by 4 if(pdu2.options().ispresent()) { int remainder = pdu2.options()().lengthof() % 4; if(remainder > 0) { pdu2.options()() = pdu2.options()() + int2oct(0,4-remainder); } } // Automatically Calculate data_offset if pl__autoDataOffset is true (default) if(pl__autoDataOffset) { if (pdu2.options().ispresent()) { pdu2.data__offset() = 5 + (pdu2.options()().lengthof())/4; } else { pdu2.data__offset() = 5; } } // RAW Encode PDU pdu2.encode(PDU__TCP_descr_, bb, TTCN_EncDec::CT_RAW); OCTETSTRING PDU2 = OCTETSTRING(bb.get_len(), bb.get_data()); //calculate checksum and put it in stream if(pl__autoChecksum) { unsigned char ip_type; if ((pl__ip__source.lengthof() == 4) && (pl__ip__dest.lengthof() == 4)) ip_type = 4; else if ((pl__ip__source.lengthof() == 16) && (pl__ip__dest.lengthof() == 16)) ip_type = 6; else TTCN_error("Source and Destination IP addresses are not both IPv4 or IPv6"); unsigned char * pdu2_stream = (unsigned char * )(const unsigned char *) PDU2; pdu2_stream[16] = 0x00; pdu2_stream[17] = 0x00; unsigned char checksum[2]; if (ip_type == 4) { calc_TCP_checksum_IPv4( (const unsigned char *)pl__ip__source, (const unsigned char *)pl__ip__dest, (const unsigned char *)pdu2_stream, bb.get_len(), checksum ); } else // IPv6 { calc_TCP_checksum_IPv6( (const unsigned char *)pl__ip__source, (const unsigned char *)pl__ip__dest, (const unsigned char *)pdu2_stream, bb.get_len(), checksum ); } pdu2_stream[16] = checksum[0]; pdu2_stream[17] = checksum[1]; } bb.clear(); if (TTCN_Logger::log_this_event(TTCN_DEBUG)) { TTCN_Logger::begin_event(TTCN_DEBUG); TTCN_Logger::log_event("Encoded PDU_TCP: "); PDU2.log(); TTCN_Logger::end_event(); } Log_function_name_on_leave(); return PDU2; } PDU__TCP f__dec__PDU__TCP(OCTETSTRING const &stream) { Log_function_name_on_enter(); if (TTCN_Logger::log_this_event(TTCN_DEBUG)) { TTCN_Logger::begin_event(TTCN_DEBUG); TTCN_Logger::log_event("Decoding PDU_TCP: "); stream.log(); TTCN_Logger::end_event(); } const unsigned char *raw_data = (const unsigned char *) stream; PDU__TCP tcp_packet; TTCN_Buffer bb; bb.put_s(20,raw_data); tcp_packet.decode(PDU__TCP_descr_, bb, TTCN_EncDec::CT_RAW); int data_length = stream.lengthof() - tcp_packet.data__offset()*4; int options_length = stream.lengthof() - data_length - 20; if (options_length == 0) tcp_packet.options() = OMIT_VALUE; else tcp_packet.options()() = OCTETSTRING(options_length,raw_data + 20 ); if (data_length == 0) tcp_packet.data() = OMIT_VALUE; else tcp_packet.data()() = OCTETSTRING(data_length,raw_data + 20 + options_length); bb.clear(); Log_object(tcp_packet); if (TTCN_Logger::log_this_event(TTCN_DEBUG)) { TTCN_Logger::begin_event(TTCN_DEBUG); TTCN_Logger::log_event("Decoded PDU_TCP: "); tcp_packet.log(); TTCN_Logger::end_event(); } Log_function_name_on_leave(); return tcp_packet; } BOOLEAN f__TCP__verify__checksum ( const OCTETSTRING& stream, const OCTETSTRING& pl__ip__source, // from IPv6 header const OCTETSTRING& pl__ip__dest // from IPv6 header ) { Log_function_name_on_enter(); unsigned char ip_type; if ((pl__ip__source.lengthof() == 4) && (pl__ip__dest.lengthof() == 4)) ip_type = 4; else if ((pl__ip__source.lengthof() == 16) && (pl__ip__dest.lengthof() == 16)) ip_type = 6; else TTCN_error("Source and Destination IP addresses are not both IPv4 or IPv6"); unsigned char * pdu2_stream = (unsigned char * )(const unsigned char *) stream; unsigned char received_checksum[2]; received_checksum[0] = pdu2_stream[16]; received_checksum[1] = pdu2_stream[17]; pdu2_stream[16] = 0x00; pdu2_stream[17] = 0x00; unsigned char calculated_checksum[2]; if (ip_type == 4) { calc_TCP_checksum_IPv4( (const unsigned char *)pl__ip__source, (const unsigned char *)pl__ip__dest, (const unsigned char *)stream, stream.lengthof(), calculated_checksum ); } else // IPv6 { calc_TCP_checksum_IPv6( (const unsigned char *)pl__ip__source, (const unsigned char *)pl__ip__dest, (const unsigned char *)stream, stream.lengthof(), calculated_checksum ); } if ((received_checksum[0] == calculated_checksum[0]) && (received_checksum[1] == calculated_checksum[1])) { Log_function_name_on_leave(); return TRUE; } else { TTCN_warning("Incorrect checksum received! \n Expected checksum: %x %x \n Received checksum: %x %x ", calculated_checksum[0], calculated_checksum[1], received_checksum[0], received_checksum[1] ); Log_function_name_on_leave(); return FALSE; } } }