From: Oleksandr Stepanov -X (ostepano - SOFTSERVE INC at Cisco) Date: Tue, 9 Sep 2025 01:47:59 +0000 (+0000) Subject: Pull request #4872: s7comm: added stream splitter abort checks X-Git-Tag: 3.9.6.0~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=47d1dee834d24dcace8b13f9d61a8fb699419abe;p=thirdparty%2Fsnort3.git Pull request #4872: s7comm: added stream splitter abort checks Merge in SNORT/snort3 from ~OSTEPANO/snort3:s7_splitter to master Squashed commit of the following: commit 9b5693da71faf7dc68d1ef55f219ede6f4c54128 Author: Oleksandr Stepanov Date: Thu Aug 14 05:14:39 2025 -0400 s7comm: added stream splitter abort checks --- diff --git a/src/service_inspectors/s7commplus/CMakeLists.txt b/src/service_inspectors/s7commplus/CMakeLists.txt index 6280158d1..961a7b7ba 100644 --- a/src/service_inspectors/s7commplus/CMakeLists.txt +++ b/src/service_inspectors/s7commplus/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(test) + set( FILE_LIST s7comm.cc s7comm.h diff --git a/src/service_inspectors/s7commplus/s7comm_decode.cc b/src/service_inspectors/s7commplus/s7comm_decode.cc index 13f91c7d7..0b6fc15d7 100644 --- a/src/service_inspectors/s7commplus/s7comm_decode.cc +++ b/src/service_inspectors/s7commplus/s7comm_decode.cc @@ -116,7 +116,7 @@ bool S7commplusDecode(Packet* p, S7commplusFlowData* mfd) const S7commplusHeader* s7commplus_header; uint16_t tpkt_length; - if (p->dsize < TPKT_MIN_HDR_LEN) + if (p->dsize < TPKT_MIN_PACKET_LEN) return false; tpkt_header = (const TpktHeader*)p->data; @@ -124,11 +124,11 @@ bool S7commplusDecode(Packet* p, S7commplusFlowData* mfd) tpkt_length = ntohs(tpkt_header->length); /* It might be a TPKT/COTP packet for other purpose, e.g. connect */ - if (cotp_header->length != COTP_HDR_LEN_FOR_S7COMMPLUS|| - cotp_header->pdu_type != COTP_HDR_PDU_TYPE_DATA) + if (cotp_header->length != COTP_MIN_PACKET_LEN || + (cotp_header->pdu_type >> 4) != COTP_DATA_TRANSFER_TPDU) return true; /* It might be COTP fragment data */ - if (tpkt_length == TPKT_MIN_HDR_LEN) + if (tpkt_length == TPKT_MIN_PACKET_LEN) { mfd->reset(); return true; diff --git a/src/service_inspectors/s7commplus/s7comm_decode.h b/src/service_inspectors/s7commplus/s7comm_decode.h index 9c798328a..c86ffc65c 100644 --- a/src/service_inspectors/s7commplus/s7comm_decode.h +++ b/src/service_inspectors/s7commplus/s7comm_decode.h @@ -29,26 +29,30 @@ struct Packet; class S7commplusFlowData; +/* TPKT defines */ +#define TPKT_SUPPORTED_VERSION 0x03 +#define TPKT_MIN_PACKET_LEN 7 + +/* COTP defines */ +#define COTP_MIN_PACKET_LEN 2 +#define COTP_CONNECTION_REQUEST_TPDU 0x0E +#define COTP_DATA_TRANSFER_TPDU 0x0F + /* S7comm defines */ #define S7COMMPLUS_PDUTYPE_CONNECT 0x01 #define S7COMMPLUS_PDUTYPE_DATA 0x02 #define S7COMMPLUS_PDUTYPE_DATAFW1_5 0x03 #define S7COMMPLUS_PDUTYPE_KEEPALIVE 0xFF -#define COTP_HDR_LEN_FOR_S7COMMPLUS 2 -#define COTP_HDR_PDU_TYPE_DATA 0xF0 - #define S7COMM_PROTOCOL_ID 0x32 -#define S7COMMPLUS_PROTOCOL_ID 0x72 +#define S7COMMPLUS_PROTOCOL_ID 0x72 -#define TPKT_MIN_HDR_LEN 7 /* length field in TPKT header for S7comm */ -#define TPKT_MAX_HDR_LEN /* Undecided */ #define S7COMMPLUS_MIN_HDR_LEN 4 #define HDR_VERSION_TWO 0x02 #define INTEGRITY_PART_LEN 33 /* length of Integrity part in V3 Header packets */ /* Need 8 bytes for MBAP Header + Function Code */ -#define S7COMMPLUS_MIN_LEN 8 this value needs to be decided +#define S7COMMPLUS_MIN_LEN 8 // this value needs to be decided /* GIDs, SIDs, and Strings */ #define GENERATOR_SPP_S7COMMPLUS 149 /* matches generators.h */ diff --git a/src/service_inspectors/s7commplus/s7comm_paf.cc b/src/service_inspectors/s7commplus/s7comm_paf.cc index b620a4b90..ab9eeba31 100644 --- a/src/service_inspectors/s7commplus/s7comm_paf.cc +++ b/src/service_inspectors/s7commplus/s7comm_paf.cc @@ -34,7 +34,8 @@ using namespace snort; -#define S7COMMPLUS_MIN_HDR_LEN 4 // Enough for Unit ID + Function + +static std::vector cotp_invalid_codes { 0x00, 0x03, 0x09, 0x0A }; S7commplusSplitter::S7commplusSplitter(bool b) : StreamSplitter(b) { @@ -44,54 +45,169 @@ S7commplusSplitter::S7commplusSplitter(bool b) : StreamSplitter(b) // S7comm/TCP PAF: // Statefully inspects S7comm traffic from the start of a session, -// Reads up until the length octet is found, then sets a flush point. +// Processes network packets byte-by-byte to validate and parse encapsulation protocol headers. +// Sets flush point at successful find of S7COMMPLUS protocol ID inside TPKT and COTP encapsulation. StreamSplitter::Status S7commplusSplitter::scan( Packet*, const uint8_t* data, uint32_t len, uint32_t /*flags*/, uint32_t* fp) { uint32_t bytes_processed = 0; + uint8_t* processed_data = const_cast(data); /* Process this packet 1 byte at a time */ while (bytes_processed < len) { switch (state) { - /* Skip the Transaction & Protocol IDs */ case S7COMMPLUS_PAF_STATE__TPKT_VER: - case S7COMMPLUS_PAF_STATE__TPKT_RESERVED: - case S7COMMPLUS_PAF_STATE__COTP_LEN: - case S7COMMPLUS_PAF_STATE__COTP_PDU_TYPE: - state = (s7commplus_paf_state_t)(((int)state) + 1); //Set the state to next PAF - // state + { + uint8_t tpkt_version = *processed_data; + if ( tpkt_version != TPKT_SUPPORTED_VERSION) + { + reset_state(); + return StreamSplitter::ABORT; + } + + ++state; break; + } + case S7COMMPLUS_PAF_STATE__TPKT_RESERVED: + { + uint8_t tpkt_reserved_bytes = *processed_data; + if (tpkt_reserved_bytes != 0) + { + reset_state(); + return StreamSplitter::ABORT; + } - /* Read length 1 byte at a time, in case a TCP segment is sent - * with xxx bytes from the S7CPAP header */ + ++state; + break; + } case S7COMMPLUS_PAF_STATE__TPKT_LEN_1: - tpkt_length |= ( *(data + bytes_processed) << 8 ); - state = S7COMMPLUS_PAF_STATE__TPKT_LEN_2; + { + tpkt_length = *(processed_data) << 8; + ++state; break; - + } case S7COMMPLUS_PAF_STATE__TPKT_LEN_2: - tpkt_length |= *(data + bytes_processed); - state = S7COMMPLUS_PAF_STATE__COTP_LEN; + { + tpkt_length |= *(processed_data); + if (tpkt_length < TPKT_MIN_PACKET_LEN) + { + reset_state(); + return StreamSplitter::ABORT; + } + + ++state; + break; + } + case S7COMMPLUS_PAF_STATE__COTP_LEN: + { + uint8_t cotp_length = *(processed_data); + if (cotp_length < COTP_MIN_PACKET_LEN) + { + reset_state(); + return StreamSplitter::ABORT; + } + + ++state; + break; + } + case S7COMMPLUS_PAF_STATE__COTP_PDU_TYPE: + { + uint8_t cotp_tpdu_and_flags = *(processed_data); + uint8_t cotp_tpdu = cotp_tpdu_and_flags >> 4; + uint8_t flags = cotp_tpdu_and_flags & 0x0F; // get lower 4 bits + + if ( (std::any_of(cotp_invalid_codes.begin(), cotp_invalid_codes.end(), + [&cotp_tpdu](uint8_t code) { return cotp_tpdu == code; })) or flags) + { + reset_state(); + return StreamSplitter::ABORT; + } + + if (cotp_tpdu == COTP_CONNECTION_REQUEST_TPDU) + { + state = S7COMMPLUS_PAF_STATE__COTP_CR_DST_REF_1; + } + else if ( cotp_tpdu == COTP_DATA_TRANSFER_TPDU ) + { + state = S7COMMPLUS_PAF_STATE__COTP_DT_TPDU_NUM_EOT; + } + else + { + *fp = tpkt_length; + reset_state(); + return StreamSplitter::FLUSH; + } + break; + } + case S7COMMPLUS_PAF_STATE__COTP_CR_DST_REF_1: + case S7COMMPLUS_PAF_STATE__COTP_CR_DST_REF_2: + case S7COMMPLUS_PAF_STATE__COTP_CR_SRC_REF_1: + case S7COMMPLUS_PAF_STATE__COTP_CR_SRC_REF_2: + ++state; + break; + + case S7COMMPLUS_PAF_STATE__COTP_CR_CLASS_OPTIONS: + { + uint8_t cotp_cr_class_options = *processed_data; - case S7COMMPLUS_PAF_STATE__SET_FLUSH: - if ((tpkt_length < TPKT_MIN_HDR_LEN)) + if (!cotp_cr_class_options or (cotp_cr_class_options == 0x10)) // Class 0 or 1 { - DetectionEngine::queue_event(GID_S7COMMPLUS, S7COMMPLUS_BAD_LENGTH); + *fp = tpkt_length; + reset_state(); + return StreamSplitter::FLUSH; + } + else + { + reset_state(); + return StreamSplitter::ABORT; + } + } + case S7COMMPLUS_PAF_STATE__COTP_DT_TPDU_NUM_EOT: + { + auto dst_ref_last_packet = *processed_data; + if (dst_ref_last_packet & 0x80) // 1st bit indicates that data is finished + ++state; + else + { + //COTP fragment, flush and wait for data + *fp = tpkt_length; + reset_state(); + return StreamSplitter::FLUSH; + } + break; + } + case S7COMMPLUS_PAF_STATE__S7_PROTOCOL_ID: + { + uint8_t s7_protocol_id = *processed_data; + if (s7_protocol_id != S7COMMPLUS_PROTOCOL_ID) + { + reset_state(); + return StreamSplitter::ABORT; } - *fp = tpkt_length; // flush point at the end of payload - state = S7COMMPLUS_PAF_STATE__TPKT_VER; - tpkt_length = 0; + *fp = tpkt_length; + reset_state(); return StreamSplitter::FLUSH; } + default: + assert(false); + reset_state(); + return StreamSplitter::ABORT; + } bytes_processed++; + processed_data++; } return StreamSplitter::SEARCH; } +void S7commplusSplitter::reset_state() +{ + tpkt_length = 0; + state = S7COMMPLUS_PAF_STATE__TPKT_VER; +} diff --git a/src/service_inspectors/s7commplus/s7comm_paf.h b/src/service_inspectors/s7commplus/s7comm_paf.h index 349467c9b..a38c17450 100644 --- a/src/service_inspectors/s7commplus/s7comm_paf.h +++ b/src/service_inspectors/s7commplus/s7comm_paf.h @@ -26,7 +26,7 @@ #include "stream/stream_splitter.h" -enum s7commplus_paf_state_t +enum s7commplus_paf_state_t : uint8_t { S7COMMPLUS_PAF_STATE__TPKT_VER = 0, S7COMMPLUS_PAF_STATE__TPKT_RESERVED, @@ -34,9 +34,29 @@ enum s7commplus_paf_state_t S7COMMPLUS_PAF_STATE__TPKT_LEN_2, S7COMMPLUS_PAF_STATE__COTP_LEN, S7COMMPLUS_PAF_STATE__COTP_PDU_TYPE, - S7COMMPLUS_PAF_STATE__SET_FLUSH + S7COMMPLUS_PAF_STATE__COTP_CR_DST_REF_1, + S7COMMPLUS_PAF_STATE__COTP_CR_DST_REF_2, + S7COMMPLUS_PAF_STATE__COTP_CR_SRC_REF_1, + S7COMMPLUS_PAF_STATE__COTP_CR_SRC_REF_2, + S7COMMPLUS_PAF_STATE__COTP_CR_CLASS_OPTIONS, + S7COMMPLUS_PAF_STATE__COTP_DT_TPDU_NUM_EOT, + S7COMMPLUS_PAF_STATE__S7_PROTOCOL_ID, + S7COMMPLUS_PAF_STATE__MAX }; +inline s7commplus_paf_state_t& operator++(s7commplus_paf_state_t& state) +{ + if(state >= S7COMMPLUS_PAF_STATE__MAX) + { + state = S7COMMPLUS_PAF_STATE__MAX; + } + else + { + state = static_cast(static_cast(state) + 1); + } + return state; +} + class S7commplusSplitter : public snort::StreamSplitter { public: @@ -48,6 +68,9 @@ public: bool is_paf() override { return true; } private: + + void reset_state(); + s7commplus_paf_state_t state; uint16_t tpkt_length; }; diff --git a/src/service_inspectors/s7commplus/test/CMakeLists.txt b/src/service_inspectors/s7commplus/test/CMakeLists.txt new file mode 100644 index 000000000..d5b9e6135 --- /dev/null +++ b/src/service_inspectors/s7commplus/test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_cpputest( s7comm_paf_test + SOURCES ../s7comm_paf.cc +) \ No newline at end of file diff --git a/src/service_inspectors/s7commplus/test/s7comm_paf_test.cc b/src/service_inspectors/s7commplus/test/s7comm_paf_test.cc new file mode 100644 index 000000000..03d1b295d --- /dev/null +++ b/src/service_inspectors/s7commplus/test/s7comm_paf_test.cc @@ -0,0 +1,280 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2025 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// +// s7comm_paf_test.cc author Oleksandr Stepanov + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../s7comm_paf.h" +#include "protocols/packet.h" + +#include +#include +#include + +namespace snort +{ +Packet::Packet(bool) { } +Packet::~Packet() = default; +const StreamBuffer StreamSplitter::reassemble(Flow*, unsigned int, unsigned int, + unsigned char const*, unsigned int, unsigned int, unsigned int &) { return {}; } +unsigned StreamSplitter::max(snort::Flow*) { return 0; } +} + + +S7commplusSplitter* test_splitter = nullptr; +snort::Packet mock_packet(true); + +TEST_GROUP(s7commplus_stream_splitter_tests) +{ + void setup() override + { + test_splitter = new S7commplusSplitter(true); + } + void teardown() override + { + delete test_splitter; + } +}; + +TEST(s7commplus_stream_splitter_tests, test_splitter_is_paf) +{ + CHECK_TRUE(test_splitter->is_paf()); +} + +TEST(s7commplus_stream_splitter_tests, splitter_search_on_not_enough_bytes) +{ + const uint8_t* test_data = (uint8_t*)"\x03"; + uint32_t test_data_len = 1; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::SEARCH); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_incorrect_tpkt_version) +{ + const uint8_t* test_data = (uint8_t*)"\x01"; + uint32_t test_data_len = 1; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_incorrect_reserved_bytes) +{ + const uint8_t* test_data = (uint8_t*)"\x03\xff\xff"; + uint32_t test_data_len = 3; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_incorrect_tpkt_length) +{ + const uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x00\x00"; + uint32_t test_data_len = 5; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_incorrect_cotp_length) +{ + const uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x10\x01"; + uint32_t test_data_len = 5; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_incorrect_cotp_pdu_type) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x10\x02\x00"; + uint32_t test_data_len = 6; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); + + test_data = (uint8_t*)"\x03\x00\x00\x10\x02\x03"; + result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); + + test_data = (uint8_t*)"\x03\x00\x00\x10\x02\x09"; + result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); + + test_data = (uint8_t*)"\x03\x00\x00\x10\x02\x0A"; + result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_cotp_cr_class_2_or_higher) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x10\x02\x0e\x00\x00\x01\x01\x20"; + uint32_t test_data_len = 11; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); + + test_data = (uint8_t*)"\x03\x00\x00\x10\x02\x0e\x00\x00\x01\x01\x40"; + result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST(s7commplus_stream_splitter_tests, splitter_flush_on_cotp_cr_class_0_or_1) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x24\x06\xe0\x00\x00\x01\x01\x00"; + uint32_t test_data_len = 11; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::FLUSH); + CHECK_EQUAL(flush_point, 36); + + test_data = (uint8_t*)"\x03\x00\x00\x24\x06\xe0\x00\x00\x01\x01\x10"; + result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::FLUSH); + CHECK_EQUAL(flush_point, 36); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_abnormal_class_options) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x24\x06\xe0\x00\x00\x01\x01\x01"; + uint32_t test_data_len = 11; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST(s7commplus_stream_splitter_tests, splitter_flush_on_data_transfer_correct_s7_protocol) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x24\x06\xf0\x80\x72"; + uint32_t test_data_len = 8; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::FLUSH); + CHECK_EQUAL(flush_point, 36); +} + +TEST(s7commplus_stream_splitter_tests, splitter_search_and_flush_correct_s7_protocol) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00"; + uint32_t test_data_len = 2; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::SEARCH); + + test_data = (uint8_t*)"\x00\x24"; + + result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::SEARCH); + + + test_data = (uint8_t*)"\x06\xf0\x80\x72"; + test_data_len = 4; + + result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::FLUSH); + CHECK_EQUAL(flush_point, 36); +} + +TEST(s7commplus_stream_splitter_tests, splitter_flush_on_cotp_fragment) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x24\x06\xf0\x00"; + uint32_t test_data_len = 7; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::FLUSH); + CHECK_EQUAL(flush_point, 36); +} + +TEST(s7commplus_stream_splitter_tests, splitter_abort_on_data_transfer_incorrect_s7_protocol) +{ + uint8_t* test_data = (uint8_t*)"\x03\x00\x00\x24\x06\xf0\x80\x34"; + uint32_t test_data_len = 8; + uint32_t flush_point = 0; + auto result = test_splitter->scan(&mock_packet, test_data, test_data_len, 0, &flush_point); + + CHECK_EQUAL(result, snort::StreamSplitter::ABORT); +} + +TEST_GROUP(s7commplus_misc) +{ + +}; + +TEST(s7commplus_misc, verify_s7commplus_paf_state) +{ + s7commplus_paf_state_t test_state = (s7commplus_paf_state_t)0; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__TPKT_VER, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__TPKT_RESERVED, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__TPKT_LEN_1, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__TPKT_LEN_2, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_LEN, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_PDU_TYPE, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_CR_DST_REF_1, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_CR_DST_REF_2, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_CR_SRC_REF_1, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_CR_SRC_REF_2, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_CR_CLASS_OPTIONS, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__COTP_DT_TPDU_NUM_EOT, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__S7_PROTOCOL_ID, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__MAX, test_state); + ++test_state; + CHECK_EQUAL(s7commplus_paf_state_t::S7COMMPLUS_PAF_STATE__MAX, test_state); +} + +int main(int argc, char** argv) +{ + int rc = CommandLineTestRunner::RunAllTests(argc, argv); + return rc; +} \ No newline at end of file