├── .gitignore ├── .settings ├── org.eclipse.cdt.core.prefs └── org.eclipse.cdt.ui.prefs ├── AUTHORS ├── README.md ├── TODO ├── data ├── OpENerPC.stc └── opener_sample_app.eds ├── examples ├── POSIX │ ├── CMakeLists.txt │ ├── main.cc │ └── sample_application │ │ ├── cipster_user_conf.h │ │ └── sampleapplication.cc └── WINDOWS │ ├── CMakeLists.txt │ ├── main.cc │ └── sample_application │ ├── cipster_user_conf.h │ └── sampleapplication.cc ├── license.txt ├── source ├── CMakeLists.txt ├── Doxyfile ├── buildsupport │ ├── CIPster.cmake │ ├── CIPster_function_checks.cmake │ ├── FindIconv.cmake │ ├── OpENer_Tests.cmake │ └── Toolchain │ │ ├── Toolchain-EABI-ARM-Generic.cmake │ │ └── toolchain-mingw64.cmake ├── doc │ └── coding_rules │ │ └── rules.txt ├── src │ ├── CMakeLists.txt │ ├── byte_bufs.h │ ├── byte_bufs.impl │ ├── cip │ │ ├── appcontype.cc │ │ ├── appcontype.h │ │ ├── cipassembly.cc │ │ ├── cipassembly.h │ │ ├── cipattribute.cc │ │ ├── cipattribute.h │ │ ├── cipclass.cc │ │ ├── cipclass.h │ │ ├── cipcommon.cc │ │ ├── cipcommon.h │ │ ├── cipconnection.cc │ │ ├── cipconnection.h │ │ ├── cipconnectionmanager.cc │ │ ├── cipconnectionmanager.h │ │ ├── cipepath.cc │ │ ├── cipepath.h │ │ ├── ciperror.cc │ │ ├── ciperror.h │ │ ├── cipethernetlink.cc │ │ ├── cipethernetlink.h │ │ ├── cipidentity.cc │ │ ├── cipidentity.h │ │ ├── cipinstance.cc │ │ ├── cipinstance.h │ │ ├── cipmessagerouter.cc │ │ ├── cipmessagerouter.h │ │ ├── cipservice.h │ │ ├── ciptcpipinterface.cc │ │ ├── ciptcpipinterface.h │ │ ├── ciptypes.h │ │ └── cipvendors.cc │ ├── cipster_api.h │ ├── enet_encap │ │ ├── byte_bufs.cc │ │ ├── cpf.cc │ │ ├── cpf.h │ │ ├── encap.cc │ │ ├── encap.h │ │ ├── networkhandler.cc │ │ ├── networkhandler.h │ │ ├── sockaddr.cc │ │ └── sockaddr.h │ ├── g_data.cc │ ├── trace.h │ ├── typedefs.h │ └── utils │ │ ├── random.cc │ │ ├── random.h │ │ ├── strprint.cc │ │ ├── xorshiftrandom.cc │ │ └── xorshiftrandom.h └── tests │ ├── CMakeLists.txt │ ├── CTestCustom.cmake │ ├── OpENerTests.cpp │ ├── OpENerTests.h │ ├── enet_encap │ ├── CMakeLists.txt │ └── endianconvtest.cpp │ └── utils │ ├── CMakeLists.txt │ ├── randomTests.cpp │ └── xorshiftrandomtests.cpp └── uncrustify.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | api_doc/ 2 | bin/ 3 | contrib/ 4 | .project 5 | .cproject 6 | .settings/ 7 | *.*~ 8 | *~ 9 | CMakeCache.txt 10 | *.patch 11 | -------------------------------------------------------------------------------- /.settings/org.eclipse.cdt.ui.prefs: -------------------------------------------------------------------------------- 1 | #Wed Sep 02 15:07:45 CEST 2009 2 | eclipse.preferences.version=1 3 | formatter_profile=org.eclipse.cdt.ui.default.gnu_profile 4 | formatter_settings_version=1 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Kurt Schweiger 2 | Rene Smodic 3 | Alois Zoitl 4 | Jonathan Engdahl 5 | Dick Hollenbeck 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CIPster Ethernet/IP Stack in C++ 2 | 3 | ## Developer's Mailing List: 4 | 5 | Colaborative discussion is here: https://www.freelists.org/list/cipster 6 | 7 | 8 | ## Welcome to CIPster! 9 | 10 | CIPster is an EtherNet/IP™ stack for I/O adapter devices; supports multiple 11 | I/O and explicit connections; includes objects and services to make EtherNet/IP™- 12 | compliant products defined in THE ETHERNET/IP SPECIFICATION and published by 13 | ODVA (http://www.odva.org). 14 | 15 | CIPster is a C++ port of C based OpENer with additional features. C++ is a 16 | higher level language than C so many things are made easier. Easier means 17 | faster. Over time this will affect rate of evolution. 18 | 19 | Thank yous go to the original authors of OpENer. See the AUTHORS. Since OpENer 20 | code was assigned to Rockwell Automation as a copyright holder, then this 21 | CIPster code has original portions copyright to Rockwell as well, however 22 | additional contributions are owned by their respective contributors. This 23 | project does not require copyright assignment, so the resulting code is under 24 | a license to its licensees, and the licensors are its multiple contributors. 25 | Should a question about ownership arise, the git version control system has a 26 | full record of who did what. 27 | 28 | ## Requirements: 29 | 30 | CIPster has been developed to be highly portable. The default version targets PCs 31 | with a POSIX operating system and a BSD-socket network interface. To test this 32 | version we recommend a Linux PC or Windows installed. 33 | 34 | On Linux, you will need to have the following installed: 35 | 36 | * CMake 37 | * gcc 38 | * make 39 | * binutils 40 | * cmake-curses-gui or cmake-gui 41 | 42 | These should be in package repositories of most Linux distros. 43 | 44 | On Windows, you will need MingW installed. 45 | 46 | If you want to run the unit tests you will also have to download CppUTest via 47 | https://github.com/cpputest/cpputest 48 | 49 | I use JEdit as my text editor, you don't need an IDE, building from the command 50 | line is not bad with the makefiles that CMake creates. 51 | 52 | For configuring the project we recommend the use of a CMake GUI (i.e. the 53 | cmake-curses-gui or cmake-gui package on Linux). 54 | 55 | ## Compiling on Linux for POSIX: 56 | 57 | There are actually three CMake projects in the tree. The lowest level CMake 58 | project is one for the library itself that creates libeip.a. Then there are two 59 | others, one that creates a POSIX/linux executable and another a windows 60 | executable. These two latter projects nest the lower level library project as a 61 | sub project. For starters, and to get familiar with CMake's "out of tree" build 62 | support, you might build the library alone at first. Since you are building out 63 | of tree, (i.e. not in the source tree) you can simply delete the out of tree 64 | directory at any time and try something else. 65 | 66 | ### Building the Library 67 | 68 | This is not a mandatory step, but is provided as a learning experience. If you 69 | want to build a sample program, skip to *Building the Sample Programs*. 70 | 71 | Let's create our out of tree directory in /tmp, which is erased on Ubuntu 72 | after each reboot. This means it will disappear, but we don't care because 73 | building the library is easy and the out of tree build directory can be anywhere 74 | after you get the hang of it. 75 | 76 | $ mkdir /tmp/build 77 | $ cd /tmp/build 78 | $ cmake DCMAKE_BUILD_TYPE=Debug /source 79 | 80 | You will get a warning about a missing USER_INCLUDE_DIR setting. That needs 81 | to be a directory containing your modified open_user_conf.h file. For this 82 | first build, we can set it to <path-to-CIPster>/examples/POSIX/sample_application . 83 | 84 | We can set this USER_INCLUDE_DIR after the CMakeCache.txt file is created 85 | using ccmake from package cmake-curses-gui. 86 | 87 | $ sudo apt-get install cmake-curses-gui 88 | $ ccmake . 89 | 90 | While inside ccmake, edit the field USER_INCLUDE_DIR to contain 91 | "<path-to-CIPster>/examples/POSIX/sample_application". After changing the 92 | field, press c for configure and g for generate. Then quit. 93 | 94 | Or you can delete the CMakeCache.txt file and start over by simply adding in the 95 | missing USER_INCLUDE_DIR setting on the command line: 96 | 97 | $ cmake -DUSER_INCLUDE_DIR=/examples/POSIX/sample_application -DCMAKE_BUILD_TYPE=Debug /source 98 | $ make 99 | 100 | You must delete CMakeCache.txt before running cmake, but not before running ccmake. 101 | 102 | ### Building the Sample Programs 103 | 104 | Building either sample program (linux or windows) is also easy. These will each 105 | be built in their own "out of tree" build directory. (With CMake, there's never 106 | a good reason to build in the source tree, ever.) When building either sample 107 | program, the library will also be built for you within a subdirectory called 108 | build-CIPster of your build directory. It is a nested project that gets built 109 | once. (You can later trigger a rebuild of this subproject by deleting this 110 | subdirectory followed by a make of the parent project.) 111 | 112 | For a Release build instead of a Debug build, substitute *Release* for *Debug* in 113 | the following instructions. From another dedicated out of tree build directory: 114 | 115 | $ cmake -DCMAKE_BUILD_TYPE=Debug /examples/POSIX 116 | $ make 117 | 118 | Wasn't that simple? CMake is indeed king of the build tools. 119 | 120 | Then you can run the resultant program. 121 | 122 | ./sample ipaddress subnetmask gateway domainname hostaddress macaddress 123 | e.g. ./sample 192.168.0.2 255.255.255.0 192.168.0.1 test.com testdevice 00 15 C5 BF D0 87 124 | 125 | Recently USER_INCLUDE_DIR has become configurable in the outer sample projects also. 126 | This makes it possible to experiment using different settings by supplying different 127 | out of tree "cipster_user_conf.h" settings. 128 | 129 | ## Compiling on Linux for Windows 130 | 131 | You can build 32 bit or 64 bit windows libraries or programs on linux using the 132 | mingw tools with CMake. CMake supports toolchain files which can be passed in 133 | the original invocation of CMake to identify the cross compiling toolchain set. 134 | There is one supplied for 64 bit Windows, so here we build a 64 bit windows 135 | sample program, and actually run in on Linux under Wine. (You can copy the 136 | toolchain file and modify it for your toolchain. Or if building on Windows, omit 137 | it. Truth be told, it is easier to build a Windows console binary on Linux than 138 | on Windows.) On Ubuntu or Debian the mingw tools are installed with the following 139 | command: 140 | 141 | $ sudo apt install g++-mingw-w64-x86-64 142 | 143 | Then follow these steps: 144 | 145 | $ mkdir /tmp/build-win-cip 146 | $ cd /tmp/build-win-cip 147 | $ cmake -DCMAKE_TOOLCHAIN_FILE=/source/buildsupport/Toolchain/toolchain-mingw64.cmake -DCMAKE_BUILD_TYPE=Debug /examples/WINDOWS 148 | $ make 149 | 150 | Then if you have 64 bit Wine installed, simply run the program as if it were a linux binary on linux. 151 | 152 | $ ./sample.exe 153 | 154 | 155 | Directory structure: 156 | -------------------- 157 | - examples ... The platform specific example programs. 158 | - doc ... Doxygen generated documentation (has to be generated for the SVN version) and Coding rules 159 | - data ... EDS file for the default application 160 | - source 161 | - src ... the production source code 162 | - cip ... the CIP layer of the stack 163 | - cip_objects ... additional CIP objects 164 | - enet_encap ... the Ethernet encapsulation layer 165 | - utils ... utility functions 166 | - tests ... the test source code 167 | - utils ... tests for utility functions 168 | 169 | Documentation: 170 | -------------- 171 | The documentation of the functions of CIPster is part of the source code. The source 172 | packages contain the generated documentation in the directory api_doc. If you 173 | use the GIT version you will need the program Doxygen for generating the HTML 174 | documentation. You can generate the documentation by invoking doxygen from the 175 | command line in the CIPster main directory. 176 | 177 | Porting CIPster: 178 | --------------- 179 | For porting CIPster to new platforms please see the porting section in the 180 | Doxygen documentation. 181 | 182 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Volunteer contributions welcome for these: 2 | 3 | *) Enhance documentation. Note that @brief is ugly and it is being removed. 4 | We have Javadoc flag enabled in Doxygen which does basically the same thing. 5 | Need to get rid of numerous Doxygen errors. 6 | 7 | *) Revive and/or replace unit tests. 8 | 9 | *) Python bindings? 10 | 11 | *) Enhance sample applications to at least know their own IP address. 12 | 13 | *) I don't see where PIT production limiting is done. 14 | -------------------------------------------------------------------------------- /data/OpENerPC.stc: -------------------------------------------------------------------------------- 1 | StcRev Data |1.23|CT-12|OPENER|1448878251| DevData |255|0|500|500|500,500|X||ENet| 2 | [<00>]-------|CIPster PC|| 3 | General |3.16|EtherNet/IP Vol 2, Ed 1.17||1.2|Rockwell Automation/Allen-Bradley|1|65001| 4 | DevProfile |12|Communications Adapter|,04,05,06,07,08,09,10,11,15,16,29,30,32,33,43,55,243,244,245,246,67,71,72,78,79,80| 5 | PhysConf ||X| |X| | | | | |0|X| | | 6 | LEDs | | | | | | 7 | IP MAC Set | |X| |192.168.56.102| | ||b8:27:eb:83:e0:f5| | | | 8 | CRate Set |X| | |X| | || 9 | CRate Sup |X|X| |X|X| 10 | Net Behavi | | ||| | | | | | | | 11 | Cxn Behavi | | | |X| | | | | | ||| | |-1|1000|1000| | 12 | FO Path ||||||||||||| | 13 | FO-IO Data | | | | | | | | | | | || | | | | | || | | | 14 | Cfg-IO Dat |||| 15 | Reserved || 16 | Safety Data| | | | | | | | | | | | | | | | |X| | | 17 | Reserved | | 18 | Safety IO |||||||||||||15|1000|1|1|| 19 | Safety Cfg |||||||||||| | | | |||2| 20 | Reserved | | 21 | [<01>]-------------------------------------------------- 22 | CAG |X|X|X| | |X|X| 23 | CAS | | | | | | | | 24 | CAL | | | | | | | | 25 | CS |X| |X| | | 26 | CSP |||||| 27 | IAG |X|X|X|X|X|X|X| | | | | | | | | | | | 28 | IAS | | | | | | | | | | | | | | | | | | | 29 | IAL | =(1)| =(12)| =(65001)| =(1.2)| | | =(CIPster PC)| | | | | | | | | | | | 30 | IS |X|X|X| | | 31 | ISP ||0,1|||| 32 | VSA | | 33 | OPT | | | 34 | [<02>]-------------------------------------------------- 35 | CAG |X|X|X| | |X|X| 36 | CAS | | | | | | | | 37 | CAL | | | | | | | | 38 | CS |X|X| | 39 | CSP |||| 40 | IAG | | | | | 41 | IAS | | | | | 42 | IAL | | | | | 43 | IS | |X| | | 44 | ISP ||||| 45 | VSA | | 46 | OPT | | | 47 | [<04a>]-------------------------------------------------- 48 | CAG |X|X|X| | |X|X| 49 | CAS | | | | | | | | 50 | CAL | | | | | | | | 51 | CS | | |X| 52 | CSP |||| 53 | IAG | | |X|X| 54 | IAS | | |X| | 55 | IAL | | | | | 56 | IS | |X|X| | | | | 57 | ISP |||||||| 58 | VSA | | 59 | OPT | | | 60 | OPT |100| 61 | [<04b>]-------------------------------------------------- 62 | CAG |X|X|X| | |X|X| 63 | CAS | | | | | | | | 64 | CAL | | | | | | | | 65 | CS | | |X| 66 | CSP |||| 67 | IAG | | |X|X| 68 | IAS | | |X| | 69 | IAL | | | | | 70 | IS | |X|X| | | | | 71 | ISP |||||||| 72 | VSA | | 73 | OPT | | | 74 | OPT |150,152,153| 75 | [<04d>]-------------------------------------------------- 76 | CAG |X|X|X| | |X|X| 77 | CAS | | | | | | | | 78 | CAL | | | | | | | | 79 | CS | | |X| 80 | CSP |||| 81 | IAG | | |X|X| 82 | IAS | | |X| | 83 | IAL | | | | | 84 | IS | |X|X| | | | | 85 | ISP |||||||| 86 | VSA | | 87 | OPT | | | 88 | OPT |151| 89 | [<04c>]-------------------------------------------------- 90 | CAG |X|X|X| | |X|X| 91 | CAS | | | | | | | | 92 | CAL | | | | | | | | 93 | CS | | |X| 94 | CSP |||| 95 | IAG | | |X|X| 96 | IAS | | |X| | 97 | IAL | | | | | 98 | IS | |X|X| | | | | 99 | ISP |||||||| 100 | VSA | | 101 | OPT | | | 102 | OPT |154| 103 | [<06>]-------------------------------------------------- 104 | CAG |X|X|X| | |X|X| 105 | CAS | | | | | | | | 106 | CAL | | | | | | | | 107 | CS |X|X| 108 | CSP ||| 109 | IAG | | | | | | | | | | | | | 110 | IAS | | | | | | | | | | | | | 111 | IAL | | | | | | | | | | | | | 112 | IS | | |X| |X| |X| | |X| | 113 | ISP |||||||||||| 114 | VSA | | 115 | OPT | | | 116 | OPT || 117 | [<245>]-------------------------------------------------- 118 | CAG |X|X|X| | |X|X| 119 | CAS | | | | | | | | 120 | CAL | | | | | | | | 121 | CS |X|X| 122 | CSP ||| 123 | IAG |X|X|X|X|X|X|X|X| | | |X| 124 | IAS | | | | | | | | | | | | | 125 | IAL | =(1,17)| =(4)| | | | | =(1..255)| | | | | =(0..3600)| 126 | IS |X| |X|X| 127 | ISP ||||| 128 | VSA | | 129 | OPT | | | 130 | [<246>]-------------------------------------------------- 131 | CAG |X|X|X| | |X|X| 132 | CAS | | | | | | | | 133 | CAL | | | | | | | | 134 | CS |X|X| | 135 | CSP |||| 136 | IAG |X|X|X| | | | | | | | 137 | IAS | | | | | | | | | | | 138 | IAL | =(0,10,100)| |MAC address (b8:27:eb:83:e0:f5)| | | | | | | | 139 | IS |X|X| | | 140 | ISP ||||| 141 | VSA | | 142 | OPT | | | 143 | [-------------------------------------------------------- 144 | -------------------------------------------------------------------------------- /examples/POSIX/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Default to CMAKE_BUILD_TYPE = Release unless overridden on command line 2 | # http://www.cmake.org/pipermail/cmake/2008-September/023808.html 3 | if( DEFINED CMAKE_BUILD_TYPE ) 4 | set( CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Set to either \"Release\" or \"Debug\"" ) 5 | else() 6 | set( CMAKE_BUILD_TYPE Release CACHE STRING "Set to either \"Release\" or \"Debug\"" ) 7 | endif() 8 | 9 | 10 | project( "CIPster on Linux" ) 11 | 12 | 13 | include(ExternalProject) 14 | 15 | cmake_minimum_required( VERSION 2.8.3 ) 16 | 17 | 18 | set( CIPSTER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../ ) 19 | 20 | set( USER_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/sample_application 21 | CACHE PATH "Location of user specific include file (cipster_user_conf.h)" 22 | ) 23 | 24 | if( CMAKE_BUILD_TYPE STREQUAL Debug ) 25 | add_definitions( -DCIPSTER_WITH_TRACES -DCIPSTER_TRACE_LEVEL=15 ) 26 | set( TRACE_SPEC "-DCIPster_TRACES=ON" ) 27 | endif() 28 | 29 | add_definitions( -std=c++0x ) 30 | 31 | # PREFIX is for ExternalProject_Add, and tells where to build CIPster as a sub project: 32 | # below our current out of tree build directory. 33 | set( PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build-CIPster ) 34 | 35 | # build CIPster as a nested project, the result of which is libeip.a 36 | # in directory ${PREFIX} 37 | ExternalProject_Add( eip 38 | PREFIX ${PREFIX} 39 | SOURCE_DIR ${CIPSTER_DIR}/source 40 | CONFIGURE_COMMAND 41 | ${CMAKE_COMMAND} 42 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 43 | -DCMAKE_INSTALL_PREFIX=${PREFIX} 44 | -DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} 45 | -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} 46 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} 47 | -DUSER_INCLUDE_DIR=${USER_INCLUDE_DIR} 48 | ${TRACE_SPEC} # empty for non Debug CMAKE_BUILD_TYPE 49 | 50 | BUILD_COMMAND make 51 | 52 | INSTALL_COMMAND make install 53 | ) 54 | 55 | #message( "TRACE_SPEC=${TRACE_SPEC}" ) 56 | 57 | 58 | # These variables would typically be found by a CMake find EIP module, but set 59 | # them manually here 60 | set( EIP_INCLUDE_DIR ${CIPSTER_DIR}/source/src ) 61 | set( EIP_LIBRARIES ${PREFIX}/libeip.a ) 62 | 63 | 64 | if( NOT APPLE ) 65 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY}hidden" ) 66 | endif() 67 | if( NOT APPLE ) 68 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN}" ) 69 | endif() 70 | 71 | 72 | include_directories( 73 | . 74 | ${EIP_INCLUDE_DIR} 75 | ${USER_INCLUDE_DIR} 76 | ) 77 | 78 | set( PGM sample ) # name of program 79 | 80 | set( PGM_SRCS 81 | main.cc 82 | sample_application/sampleapplication.cc 83 | ) 84 | 85 | add_executable( ${PGM} 86 | ${PGM_SRCS} 87 | ) 88 | target_link_libraries( ${PGM} 89 | ${EIP_LIBRARIES} 90 | ) 91 | add_dependencies( ${PGM} eip ) 92 | 93 | -------------------------------------------------------------------------------- /examples/POSIX/main.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | #include 8 | #include 9 | 10 | #include "cipster_api.h" 11 | 12 | EipStatus ApplicationInitialization(); // my business. 13 | 14 | // **************************************************************************** 15 | /** @brief Signal handler function for ending stack execution 16 | * 17 | * @param signal the signal we received 18 | */ 19 | void LeaveStack( int signal ); 20 | 21 | // *************************************************************************** 22 | /** @brief Flag indicating if the stack should end its execution 23 | */ 24 | volatile bool g_end_stack; 25 | 26 | 27 | 28 | bool parse_mac( const char* mac_str, uint8_t mac_out[6] ) 29 | { 30 | int b[6]; 31 | 32 | if( 6 == sscanf_s( mac_str, "%x:%x:%x:%x:%x:%x", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5] ) || 33 | 6 == sscanf_s( mac_str, "%x-%x-%x-%x-%x-%x", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5] ) ) 34 | { 35 | for( int i=0; i<6; ++i ) 36 | mac_out[i] = b[i]; 37 | 38 | return true; 39 | } 40 | 41 | return false; 42 | } 43 | 44 | 45 | int main( int argc, char* argv[] ) 46 | { 47 | int ret = 0; 48 | 49 | uint8_t my_mac_address[6]; 50 | uint16_t unique_connection_id; 51 | 52 | if( argc != 7 ) 53 | { 54 | printf( "Wrong number of command line parameters! %d instead of 12\n", argc ); 55 | printf( "The correct command line parameters are:\n" ); 56 | printf( "%s ipaddress subnetmask gateway domainname hostaddress macaddress\n", argv[0] ); 57 | printf( "e.g.\n" ); 58 | printf( " %s 192.168.0.2 255.255.255.0 192.168.0.1 test.com testdevice 00:15:C5:BF:D0:87\n", argv[0] ); 59 | ret = 1; 60 | goto exit; 61 | } 62 | 63 | // unique_connection_id should be sufficiently random or incremented 64 | // and stored in non-volatile memory each time the device boots. 65 | unique_connection_id = rand(); 66 | 67 | // Setup the CIP stack early, before calling any stack Configuration functions. 68 | CipStackInit( unique_connection_id ); 69 | 70 | // fetch Internet address info from the platform 71 | ConfigureNetworkInterface( argv[1], argv[2], argv[3] ); 72 | ConfigureDomainName( argv[4] ); 73 | ConfigureHostName( argv[5] ); 74 | 75 | if( !parse_mac( argv[6], my_mac_address ) ) 76 | { 77 | printf( "Bad macaddress format. It can use either : or - to separate:\n" 78 | " e.g. 00:15:C5:BF:D0:87 or 00-15-C5-BF-D0-87\n" ); 79 | ret = 2; 80 | goto exit; 81 | } 82 | 83 | ConfigureMacAddress( my_mac_address ); 84 | 85 | // for a real device the serial number should be unique per device 86 | SetDeviceSerialNumber( 123456789 ); 87 | 88 | if( ApplicationInitialization() != kEipStatusOk ) 89 | { 90 | fprintf( stderr, "Unable to initialize Assembly instances\n" ); 91 | ret = 2; 92 | goto shutdown; 93 | } 94 | 95 | // Setup Network only after Configure*() calls above 96 | if( NetworkHandlerInitialize() != kEipStatusOk ) 97 | { 98 | fprintf( stderr, "Unable to initialize NetworkHandlers\n" ); 99 | ret = 3; 100 | goto shutdown; 101 | } 102 | 103 | #ifndef _WIN32 104 | // register for closing signals so that we can trigger the stack to end 105 | signal( SIGHUP, LeaveStack ); 106 | signal( SIGINT, LeaveStack ); 107 | #endif 108 | 109 | printf( "running...\n" ); 110 | 111 | // The event loop. Put other processing you need done continually in here 112 | while( !g_end_stack ) 113 | { 114 | if( kEipStatusOk != NetworkHandlerProcessOnce() ) 115 | { 116 | break; 117 | } 118 | } 119 | 120 | printf( "\ncleaning up and ending...\n" ); 121 | 122 | // clean up network state 123 | NetworkHandlerFinish(); 124 | 125 | shutdown: 126 | // close remaining sessions and connections, cleanup used data 127 | ShutdownCipStack(); 128 | 129 | exit: 130 | return ret; 131 | } 132 | 133 | 134 | void LeaveStack( int signal ) 135 | { 136 | (void) signal; // kill unused parameter warning 137 | 138 | CIPSTER_TRACE_STATE( "got signal\n" ); 139 | 140 | g_end_stack = true; 141 | } 142 | -------------------------------------------------------------------------------- /examples/POSIX/sample_application/cipster_user_conf.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_USER_CONF_H_ 7 | #define CIPSTER_USER_CONF_H_ 8 | 9 | /** @file cipster_user_conf.h 10 | * @brief CIPster configuration setup 11 | * 12 | * This file contains the general application specific configuration for CIPster. 13 | * 14 | * Furthermore you have to specific platform specific network include files. 15 | * CIPster needs definitions for the following data-types 16 | * and functions: 17 | * - struct sockaddr_in 18 | * - AF_INET 19 | * - INADDR_ANY 20 | * - htons 21 | * - ntohl 22 | * - inet_addr 23 | */ 24 | 25 | /// Identity configuration of the device 26 | #define CIPSTER_DEVICE_VENDOR_ID 60000 27 | #define CIPSTER_DEVICE_TYPE 12 28 | #define CIPSTER_DEVICE_PRODUCT_CODE 65001 29 | #define CIPSTER_DEVICE_MAJOR_REVISION 1 30 | #define CIPSTER_DEVICE_MINOR_REVISION 2 31 | #define CIPSTER_DEVICE_NAME "amphibius goodie" 32 | 33 | 34 | /** @brief Define the number of supported explicit connections. 35 | * According to ODVA's PUB 70 this number should be greater than 6. 36 | */ 37 | #define CIPSTER_CIP_NUM_EXPLICIT_CONNS 6 38 | 39 | /** @brief Define the number of supported exclusive owner connections. 40 | * Each of these connections has to be configured with the function 41 | * void configureExclusiveOwnerConnectionPoint(unsigned int pa_unConnNum, 42 | * unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, 43 | * unsigned int pa_unConfigAssembly ) 44 | */ 45 | #define CIPSTER_CIP_NUM_EXCLUSIVE_OWNER_CONNS 5 46 | 47 | /** @brief Define the number of supported input only connections. 48 | * Each of these connections has to be configured with the function 49 | * void configureInputOnlyConnectionPoint(unsigned int pa_unConnNum, 50 | * unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, 51 | * unsigned int pa_unConfigAssembly) 52 | */ 53 | #define CIPSTER_CIP_NUM_INPUT_ONLY_CONNS 5 54 | 55 | /** @brief Define the number of supported input only connections per connection path 56 | */ 57 | #define CIPSTER_CIP_NUM_INPUT_ONLY_CONNS_PER_CON_PATH 3 58 | 59 | /** @brief Define the number of supported listen only connections. 60 | * Each of these connections has to be configured with the function 61 | * void configureListenOnlyConnectionPoint(unsigned int pa_unConnNum, 62 | * unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, 63 | * unsigned int pa_unConfigAssembly) 64 | * 65 | */ 66 | #define CIPSTER_CIP_NUM_LISTEN_ONLY_CONNS 5 67 | 68 | /** @brief Define the number of supported Listen only connections per connection path 69 | */ 70 | #define CIPSTER_CIP_NUM_LISTEN_ONLY_CONNS_PER_CON_PATH 3 71 | 72 | /** 73 | * The number of bytes used for the Ethernet message buffer. 74 | * 75 | * This buffer size will be used for any received message. 76 | */ 77 | #define CIPSTER_ETHERNET_BUFFER_SIZE 1800 78 | 79 | /** 80 | * The number of bytes used for the buffer that will be used for generating any 81 | * reply data of messages. There are two uses in CIPster: 82 | * 1. Explicit messages will use this buffer to store the data generated by the request 83 | * 2. I/O Connections will use this buffer for the produced data 84 | */ 85 | #define CIPSTER_MESSAGE_DATA_REPLY_BUFFER 1600 86 | 87 | 88 | /** @brief Number of sessions that can be handled at the same time 89 | */ 90 | #define CIPSTER_NUMBER_OF_SUPPORTED_SESSIONS 20 91 | 92 | /** 93 | * The clock period in usecs of the timer used in this implementation. 94 | * It should be a multiple of milliseconds expressed in microseconds, 95 | * i.e. the last 3 digits *should* be zero because of 96 | * Vol1 3-4.4.9 Expected_packet_rate is expressed 97 | * in milliseconds. However, whether this is mandatory given the jitter 98 | * associated with the call frequency to NetworkHandlerProcessOnce() 99 | * seems questionable. 100 | */ 101 | const unsigned kCIPsterTimerTickInMicroSeconds = 10000; 102 | 103 | /** 104 | * The setting of this affects the real time format of 105 | * the consuming half of a kConnTransportClass0 or kConnTransportClass1 106 | * I/O connection. 107 | * Tells whether the 32 bit RUN IDLE dword is expected in consumed data. 108 | * When this is set to true, then the real time format is 109 | * kRealTimeFmt32BitHeader. When false then kRealTimeFmtModeless. 110 | * You may change these settings for every IO connection separately using 111 | * ConnectionData::SetConsumingFmt() and ConnectionData::SetProducingRTFmt() if 112 | * the defaults controlled by this constant do not meet your needs. But that 113 | * would make what the EDS file advertises as being inconsistent. 114 | * @see Vol1 3-6.1 115 | */ 116 | const bool kCIPsterConsumedDataHasRunIdleHeader = true; 117 | 118 | /** 119 | * The setting of this affects the real time format of 120 | * the producing half of a kConnTransportClass0 or kConnTransportClass1 121 | * I/O connection. 122 | * Tells whether the 32 bit RUN IDLE dword is expected in produced data. 123 | * When this is set to true, then the real time format is 124 | * kRealTimeFmt32BitHeader. When false then kRealTimeFmtModeless. 125 | * You may change these settings for every IO connection separately using 126 | * ConnectionData::SetConsumingFmt() and ConnectionData::SetProducingRTFmt() if 127 | * the defaults controlled by this constant do not meet your needs. But that 128 | * would make what the EDS file advertises as being inconsistent. 129 | * @see Vol1 3-6.1 130 | */ 131 | const bool kCIPsterProducedDataHasRunIdleHeader = false; 132 | 133 | /** 134 | * The setting of this affects point to point connections with a FANUC robot 135 | * configured as a scanner by adding an O->T Saii in the forward_open reply 136 | * even if the port is 0x08AE. FANUC always needs this parameter to work 137 | * properly. 138 | * @see Vol2 3-3.9.6 139 | */ 140 | const bool kCIPsterFanucScanner = false; 141 | 142 | #ifdef CIPSTER_WITH_TRACES 143 | // If we have tracing enabled provide print tracing macro 144 | #include 145 | 146 | #define LOG_TRACE(...) printf(__VA_ARGS__) 147 | 148 | #ifndef NDEBUG // for "Debug" builds 149 | 150 | #if 0 151 | #define CIPSTER_ASSERT(assertion) \ 152 | do { \ 153 | if(!(assertion)) { \ 154 | LOG_TRACE("Assertion \"%s\" failed: file \"%s\", line %d\n", #assertion, __FILE__, __LINE__); \ 155 | while(1){;} \ 156 | } \ 157 | } while(0) 158 | #else 159 | #include 160 | #define CIPSTER_ASSERT(assertion) assert(assertion) 161 | #endif 162 | 163 | // could use standard assert() 164 | //#include 165 | //#define CIPSTER_ASSERT(assertion) assert(assertion) 166 | 167 | #else // for "Release" builds 168 | 169 | #define CIPSTER_ASSERT(x) // nothing 170 | #endif // NDEBUG 171 | 172 | #else // no CIPSTER_WITH_TRACES 173 | #define LOG_TRACE(x) // nothing 174 | #define CIPSTER_ASSERT(x) // nothing 175 | #endif 176 | 177 | #endif // CIPSTER_USER_CONF_H_ 178 | -------------------------------------------------------------------------------- /examples/POSIX/sample_application/sampleapplication.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define DEMO_APP_INPUT_ASSEMBLY_NUM 100 // 0x064 12 | #define DEMO_APP_OUTPUT_ASSEMBLY_NUM 150 // 0x096 13 | #define DEMO_APP_CONFIG_ASSEMBLY_NUM 151 // 0x097 14 | #define DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM 152 // 0x098 15 | #define DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM 153 // 0x099 16 | #define DEMO_APP_EXPLICT_ASSEMBLY_NUM 154 // 0x09A 17 | 18 | // global variables for demo application (4 assembly data fields) *********** 19 | 20 | uint8_t g_assembly_data064[128]; // Input 21 | uint8_t g_assembly_data096[128]; // Output 22 | uint8_t g_assembly_data097[64]; // Config 23 | uint8_t g_assembly_data09A[128]; // Explicit 24 | 25 | 26 | EipStatus ApplicationInitialization() 27 | { 28 | // create 3 assembly object instances 29 | // INPUT 30 | CreateAssemblyInstance( DEMO_APP_INPUT_ASSEMBLY_NUM, 31 | ByteBuf( g_assembly_data064, sizeof(g_assembly_data064) ) ); 32 | 33 | // OUTPUT 34 | CreateAssemblyInstance( DEMO_APP_OUTPUT_ASSEMBLY_NUM, 35 | ByteBuf( g_assembly_data096, sizeof(g_assembly_data096) ) ); 36 | 37 | // CONFIG 38 | CreateAssemblyInstance( DEMO_APP_CONFIG_ASSEMBLY_NUM, 39 | ByteBuf( g_assembly_data097, sizeof(g_assembly_data097) ) ); 40 | 41 | // Heart-beat output assembly for Input only connections 42 | CreateAssemblyInstance( DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM, 43 | ByteBuf( 0, 0 ) ); 44 | 45 | // Heart-beat output assembly for Listen only connections 46 | CreateAssemblyInstance( DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM, 47 | ByteBuf( 0, 0 ) ); 48 | 49 | // assembly for explicit messaging 50 | CreateAssemblyInstance( DEMO_APP_EXPLICT_ASSEMBLY_NUM, 51 | ByteBuf( g_assembly_data09A, sizeof(g_assembly_data09A) ) ); 52 | 53 | // Reserve some connection instances for the above assemblies: 54 | 55 | ConfigureExclusiveOwnerConnectionPoint( 56 | DEMO_APP_OUTPUT_ASSEMBLY_NUM, 57 | DEMO_APP_INPUT_ASSEMBLY_NUM, 58 | DEMO_APP_CONFIG_ASSEMBLY_NUM ); 59 | 60 | // Reserve a connection instance that can connect without a config_path 61 | ConfigureExclusiveOwnerConnectionPoint( 62 | DEMO_APP_OUTPUT_ASSEMBLY_NUM, 63 | DEMO_APP_INPUT_ASSEMBLY_NUM, 64 | -1 ); // config path may be omitted 65 | 66 | ConfigureInputOnlyConnectionPoint( 67 | DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM, 68 | DEMO_APP_INPUT_ASSEMBLY_NUM, 69 | DEMO_APP_CONFIG_ASSEMBLY_NUM ); 70 | 71 | ConfigureListenOnlyConnectionPoint( 72 | DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM, 73 | DEMO_APP_INPUT_ASSEMBLY_NUM, 74 | DEMO_APP_CONFIG_ASSEMBLY_NUM ); 75 | 76 | return kEipStatusOk; 77 | } 78 | 79 | 80 | void HandleApplication() 81 | { 82 | // check if application needs to trigger a connection 83 | } 84 | 85 | 86 | void NotifyIoConnectionEvent( CipConn* aConn, IoConnectionEvent io_connection_event ) 87 | { 88 | // maintain a correct output state according to the connection state 89 | int consuming_id = aConn->ConsumingPath().GetInstanceOrConnPt(); 90 | int producing_id = aConn->ProducingPath().GetInstanceOrConnPt(); 91 | } 92 | 93 | 94 | EipStatus AfterAssemblyDataReceived( AssemblyInstance* aInstance, 95 | OpMode aMode, int aBytesReceivedCount ) 96 | { 97 | EipStatus status = kEipStatusOk; 98 | 99 | // handle the data received e.g., update outputs of the device 100 | switch( aInstance->Id() ) 101 | { 102 | case DEMO_APP_OUTPUT_ASSEMBLY_NUM: 103 | // Data for the output assembly has been received. 104 | // Mirror it to the inputs 105 | memcpy( &g_assembly_data064[0], &g_assembly_data096[0], 106 | sizeof(g_assembly_data064) ); 107 | break; 108 | 109 | case DEMO_APP_EXPLICT_ASSEMBLY_NUM: 110 | // do something interesting with the new data from 111 | // the explicit set-data-attribute message 112 | break; 113 | 114 | case DEMO_APP_CONFIG_ASSEMBLY_NUM: 115 | /* Add here code to handle configuration data and check if it is ok 116 | * The demo application does not handle config data. 117 | * However in order to pass the test we accept any data given. 118 | * EIP_ERROR 119 | */ 120 | status = kEipStatusOk; 121 | break; 122 | } 123 | 124 | return status; 125 | } 126 | 127 | 128 | bool BeforeAssemblyDataSend( AssemblyInstance* instance ) 129 | { 130 | // update data to be sent e.g., read inputs of the device 131 | /*In this sample app we mirror the data from out to inputs on data receive 132 | * therefore we need nothing to do here. Just return true to inform that 133 | * the data is new. 134 | */ 135 | 136 | if( instance->Id() == DEMO_APP_EXPLICT_ASSEMBLY_NUM ) 137 | { 138 | /* do something interesting with the existing data 139 | * for the explicit get-data-attribute message */ 140 | } 141 | 142 | return true; 143 | } 144 | 145 | 146 | EipStatus ResetDevice() 147 | { 148 | // add reset code here 149 | return kEipStatusOk; 150 | } 151 | 152 | 153 | EipStatus ResetDeviceToInitialConfiguration( bool also_reset_comm_params ) 154 | { 155 | // reset the parameters 156 | 157 | // then perform device reset 158 | 159 | return kEipStatusOk; 160 | } 161 | 162 | 163 | void RunIdleChanged( uint32_t run_idle_value ) 164 | { 165 | (void) run_idle_value; 166 | } 167 | -------------------------------------------------------------------------------- /examples/WINDOWS/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Default to CMAKE_BUILD_TYPE = Release unless overridden on command line 2 | # http://www.cmake.org/pipermail/cmake/2008-September/023808.html 3 | if( DEFINED CMAKE_BUILD_TYPE ) 4 | set( CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Set to either \"Release\" or \"Debug\"" ) 5 | else() 6 | set( CMAKE_BUILD_TYPE Release CACHE STRING "Set to either \"Release\" or \"Debug\"" ) 7 | endif() 8 | 9 | 10 | project( "CIPster on Windows" ) 11 | 12 | 13 | include(ExternalProject) 14 | 15 | cmake_minimum_required( VERSION 2.8.3 ) 16 | 17 | 18 | set( CIPSTER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../ ) 19 | 20 | set( USER_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/sample_application 21 | CACHE PATH "Location of user specific include file (cipster_user_conf.h)" 22 | ) 23 | 24 | if( CMAKE_BUILD_TYPE STREQUAL Debug ) 25 | add_definitions( -DCIPSTER_WITH_TRACES -DCIPSTER_TRACE_LEVEL=15 ) 26 | set( TRACE_SPEC "-DCIPster_TRACES=ON" ) 27 | endif() 28 | 29 | add_definitions( -std=c++0x ) 30 | 31 | # PREFIX is for ExternalProject_Add, and tells where to build CIPster as a sub project: 32 | # below our current out of tree build directory. 33 | set( PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build-CIPster ) 34 | 35 | # build CIPster as a nested project, the result of which is libeip.a 36 | # in directory ${PREFIX} 37 | ExternalProject_Add( eip 38 | PREFIX ${PREFIX} 39 | SOURCE_DIR ${CIPSTER_DIR}/source 40 | CONFIGURE_COMMAND 41 | ${CMAKE_COMMAND} 42 | -DCMAKE_GENERATOR=${CMAKE_GENERATOR} 43 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 44 | -DCMAKE_INSTALL_PREFIX=${PREFIX} 45 | -DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} 46 | -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} 47 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} 48 | -DUSER_INCLUDE_DIR=${USER_INCLUDE_DIR} 49 | ${TRACE_SPEC} # empty for non Debug CMAKE_BUILD_TYPE 50 | 51 | BUILD_COMMAND make 52 | 53 | INSTALL_COMMAND make install 54 | ) 55 | 56 | #message( "TRACE_SPEC=${TRACE_SPEC}" ) 57 | 58 | 59 | # These variables would typically be found by a CMake find EIP module, but set 60 | # them manually here 61 | set( EIP_INCLUDE_DIR ${CIPSTER_DIR}/source/src ) 62 | set( EIP_LIBRARIES ${PREFIX}/libeip.a ) 63 | 64 | 65 | if( NOT APPLE ) 66 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY}hidden" ) 67 | endif() 68 | if( NOT APPLE ) 69 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN}" ) 70 | endif() 71 | 72 | 73 | if( CMAKE_COMPILER_IS_GNUCXX ) 74 | # statically link the gcc and socket libraries so that dynamic ones do not have to be 75 | # installed on the ultimate target separately. 76 | set( CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -static ) 77 | endif() 78 | 79 | 80 | include_directories( 81 | . 82 | ${EIP_INCLUDE_DIR} 83 | ${USER_INCLUDE_DIR} 84 | ) 85 | 86 | set( PGM sample ) # name of program 87 | 88 | set( PGM_SRCS 89 | main.cc 90 | sample_application/sampleapplication.cc 91 | ) 92 | 93 | add_executable( ${PGM} 94 | ${PGM_SRCS} 95 | ) 96 | target_link_libraries( ${PGM} 97 | ${EIP_LIBRARIES} 98 | ws2_32 99 | ) 100 | add_dependencies( ${PGM} eip ) 101 | 102 | -------------------------------------------------------------------------------- /examples/WINDOWS/main.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | #include 8 | #include 9 | 10 | #include "cipster_api.h" 11 | 12 | EipStatus ApplicationInitialization(); // my business. 13 | 14 | /** @brief Signal handler function for ending stack execution 15 | * 16 | * @param signal the signal we received 17 | */ 18 | void LeaveStack( int signal ); 19 | 20 | /** @brief Flag indicating if the stack should end its execution 21 | */ 22 | volatile bool g_end_stack; 23 | 24 | 25 | bool parse_mac( const char* mac_str, uint8_t mac_out[6] ) 26 | { 27 | int b[6]; 28 | 29 | if( 6 == sscanf_s( mac_str, "%x:%x:%x:%x:%x:%x", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5] ) || 30 | 6 == sscanf_s( mac_str, "%x-%x-%x-%x-%x-%x", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5] ) ) 31 | { 32 | for( int i=0; i<6; ++i ) 33 | mac_out[i] = b[i]; 34 | 35 | return true; 36 | } 37 | 38 | return false; 39 | } 40 | 41 | 42 | int main( int argc, char* argv[] ) 43 | { 44 | int ret = 0; 45 | 46 | uint8_t my_mac_address[6]; 47 | uint16_t unique_connection_id; 48 | 49 | if( argc != 7 ) 50 | { 51 | printf( "Wrong number of command line parameters! %d instead of 12\n", argc ); 52 | printf( "The correct command line parameters are:\n" ); 53 | printf( "%s ipaddress subnetmask gateway domainname hostaddress macaddress\n", argv[0] ); 54 | printf( "e.g.\n" ); 55 | printf( " %s 192.168.0.2 255.255.255.0 192.168.0.1 test.com testdevice 00:15:C5:BF:D0:87\n", argv[0] ); 56 | ret = 1; 57 | goto exit; 58 | } 59 | 60 | // unique_connection_id should be sufficiently random or incremented 61 | // and stored in non-volatile memory each time the device boots. 62 | unique_connection_id = rand(); 63 | 64 | // Setup the CIP stack early, before calling any stack Configuration functions. 65 | CipStackInit( unique_connection_id ); 66 | 67 | // fetch Internet address info from the platform 68 | ConfigureNetworkInterface( argv[1], argv[2], argv[3] ); 69 | ConfigureDomainName( argv[4] ); 70 | ConfigureHostName( argv[5] ); 71 | 72 | if( !parse_mac( argv[6], my_mac_address ) ) 73 | { 74 | printf( "Bad macaddress format. It can use either : or - to separate:\n" 75 | " e.g. 00:15:C5:BF:D0:87 or 00-15-C5-BF-D0-87\n" ); 76 | ret = 2; 77 | goto exit; 78 | } 79 | 80 | ConfigureMacAddress( my_mac_address ); 81 | 82 | // for a real device the serial number should be unique per device 83 | SetDeviceSerialNumber( 123456789 ); 84 | 85 | if( ApplicationInitialization() != kEipStatusOk ) 86 | { 87 | fprintf( stderr, "Unable to initialize Assembly instances\n" ); 88 | ret = 2; 89 | goto shutdown; 90 | } 91 | 92 | // Setup Network only after Configure*() calls above 93 | if( NetworkHandlerInitialize() != kEipStatusOk ) 94 | { 95 | fprintf( stderr, "Unable to initialize NetworkHandlers\n" ); 96 | ret = 3; 97 | goto shutdown; 98 | } 99 | 100 | #ifndef _WIN32 101 | // register for closing signals so that we can trigger the stack to end 102 | signal( SIGHUP, LeaveStack ); 103 | signal( SIGINT, LeaveStack ); 104 | #endif 105 | 106 | printf( "running...\n" ); 107 | 108 | // The event loop. Put other processing you need done continually in here 109 | while( !g_end_stack ) 110 | { 111 | if( kEipStatusOk != NetworkHandlerProcessOnce() ) 112 | { 113 | break; 114 | } 115 | } 116 | 117 | printf( "\ncleaning up and ending...\n" ); 118 | 119 | // clean up network state 120 | NetworkHandlerFinish(); 121 | 122 | shutdown: 123 | // close remaining sessions and connections, cleanup used data 124 | ShutdownCipStack(); 125 | 126 | exit: 127 | return ret; 128 | } 129 | 130 | 131 | void LeaveStack( int signal ) 132 | { 133 | (void) signal; // kill unused parameter warning 134 | 135 | CIPSTER_TRACE_STATE( "got signal\n" ); 136 | 137 | g_end_stack = true; 138 | } 139 | -------------------------------------------------------------------------------- /examples/WINDOWS/sample_application/cipster_user_conf.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_USER_CONF_H_ 7 | #define CIPSTER_USER_CONF_H_ 8 | 9 | /** @file cipster_user_conf.h 10 | * @brief CIPster configuration setup 11 | * 12 | * This file contains the general application specific configuration for CIPster. 13 | * 14 | * Furthermore you have to specific platform specific network include files. 15 | * CIPster needs definitions for the following data-types 16 | * and functions: 17 | * - struct sockaddr_in 18 | * - AF_INET 19 | * - INADDR_ANY 20 | * - htons 21 | * - ntohl 22 | * - inet_addr 23 | */ 24 | 25 | /// Identity configuration of the device 26 | #define CIPSTER_DEVICE_VENDOR_ID 60000 27 | #define CIPSTER_DEVICE_TYPE 12 28 | #define CIPSTER_DEVICE_PRODUCT_CODE 65001 29 | #define CIPSTER_DEVICE_MAJOR_REVISION 1 30 | #define CIPSTER_DEVICE_MINOR_REVISION 2 31 | #define CIPSTER_DEVICE_NAME "amphibius goodie" 32 | 33 | 34 | /** @brief Define the number of supported explicit connections. 35 | * According to ODVA's PUB 70 this number should be greater than 6. 36 | */ 37 | #define CIPSTER_CIP_NUM_EXPLICIT_CONNS 6 38 | 39 | /** @brief Define the number of supported exclusive owner connections. 40 | * Each of these connections has to be configured with the function 41 | * void configureExclusiveOwnerConnectionPoint(unsigned int pa_unConnNum, 42 | * unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, 43 | * unsigned int pa_unConfigAssembly ) 44 | */ 45 | #define CIPSTER_CIP_NUM_EXCLUSIVE_OWNER_CONNS 5 46 | 47 | /** @brief Define the number of supported input only connections. 48 | * Each of these connections has to be configured with the function 49 | * void configureInputOnlyConnectionPoint(unsigned int pa_unConnNum, 50 | * unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, 51 | * unsigned int pa_unConfigAssembly) 52 | */ 53 | #define CIPSTER_CIP_NUM_INPUT_ONLY_CONNS 5 54 | 55 | /** @brief Define the number of supported input only connections per connection path 56 | */ 57 | #define CIPSTER_CIP_NUM_INPUT_ONLY_CONNS_PER_CON_PATH 3 58 | 59 | /** @brief Define the number of supported listen only connections. 60 | * Each of these connections has to be configured with the function 61 | * void configureListenOnlyConnectionPoint(unsigned int pa_unConnNum, 62 | * unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, 63 | * unsigned int pa_unConfigAssembly) 64 | * 65 | */ 66 | #define CIPSTER_CIP_NUM_LISTEN_ONLY_CONNS 5 67 | 68 | /** @brief Define the number of supported Listen only connections per connection path 69 | */ 70 | #define CIPSTER_CIP_NUM_LISTEN_ONLY_CONNS_PER_CON_PATH 3 71 | /** 72 | * The number of bytes used for the Ethernet message buffer. 73 | * 74 | * This buffer size will be used for any received message. 75 | */ 76 | #define CIPSTER_ETHERNET_BUFFER_SIZE 1800 77 | 78 | /** 79 | * The number of bytes used for the buffer that will be used for generating any 80 | * reply data of messages. There are two uses in CIPster: 81 | * 1. Explicit messages will use this buffer to store the data generated by the request 82 | * 2. I/O Connections will use this buffer for the produced data 83 | */ 84 | #define CIPSTER_MESSAGE_DATA_REPLY_BUFFER 1600 85 | 86 | 87 | /** @brief Number of sessions that can be handled at the same time 88 | */ 89 | #define CIPSTER_NUMBER_OF_SUPPORTED_SESSIONS 20 90 | 91 | /** 92 | * The clock period in usecs of the timer used in this implementation. 93 | * It should be a multiple of milliseconds expressed in microseconds, 94 | * i.e. the last 3 digits *should* be zero because of 95 | * Vol1 3-4.4.9 Expected_packet_rate is expressed 96 | * in milliseconds. However, whether this is mandatory given the jitter 97 | * associated with the call frequency to NetworkHandlerProcessOnce() 98 | * seems questionable. 99 | */ 100 | const unsigned kCIPsterTimerTickInMicroSeconds = 10000; 101 | 102 | /** 103 | * The setting of this affects the real time format of 104 | * the consuming half of a kConnTransportClass0 or kConnTransportClass1 105 | * I/O connection. 106 | * Tells whether the 32 bit RUN IDLE dword is expected in consumed data. 107 | * When this is set to true, then the real time format is 108 | * kRealTimeFmt32BitHeader. When false then kRealTimeFmtModeless. 109 | * You may change these settings for every IO connection separately using 110 | * ConnectionData::SetConsumingFmt() and ConnectionData::SetProducingRTFmt() if 111 | * the defaults controlled by this constant do not meet your needs. But that 112 | * would make what the EDS file advertises as being inconsistent. 113 | * @see Vol1 3-6.1 114 | */ 115 | const bool kCIPsterConsumedDataHasRunIdleHeader = true; 116 | 117 | /** 118 | * The setting of this affects the real time format of 119 | * the producing half of a kConnTransportClass0 or kConnTransportClass1 120 | * I/O connection. 121 | * Tells whether the 32 bit RUN IDLE dword is expected in produced data. 122 | * When this is set to true, then the real time format is 123 | * kRealTimeFmt32BitHeader. When false then kRealTimeFmtModeless. 124 | * You may change these settings for every IO connection separately using 125 | * ConnectionData::SetConsumingFmt() and ConnectionData::SetProducingRTFmt() if 126 | * the defaults controlled by this constant do not meet your needs. But that 127 | * would make what the EDS file advertises as being inconsistent. 128 | * @see Vol1 3-6.1 129 | */ 130 | const bool kCIPsterProducedDataHasRunIdleHeader = false; 131 | 132 | /** 133 | * The setting of this affects point to point connections with a FANUC robot 134 | * configured as a scanner by adding an O->T Saii in the forward_open reply 135 | * even if the port is 0x08AE. FANUC always needs this parameter to work 136 | * properly. 137 | * @see Vol2 3-3.9.6 138 | */ 139 | const bool kCIPsterFanucScanner = false; 140 | 141 | #ifdef CIPSTER_WITH_TRACES 142 | // If we have tracing enabled provide print tracing macro 143 | #include 144 | 145 | //#define LOG_TRACE(...) fprintf(stderr,__VA_ARGS__) 146 | #define LOG_TRACE(...) printf(__VA_ARGS__) 147 | 148 | #ifndef NDEBUG // for "Debug" builds 149 | 150 | /** @brief A specialized assertion command that will log the assertion and block 151 | * further execution in an while(1) loop. 152 | */ 153 | #define CIPSTER_ASSERT(assertion) \ 154 | do { \ 155 | if(!(assertion)) { \ 156 | LOG_TRACE("Assertion \"%s\" failed: file \"%s\", line %d\n", #assertion, __FILE__, __LINE__); \ 157 | while(1){;} \ 158 | } \ 159 | } while(0) 160 | 161 | // could use standard assert() 162 | //#include 163 | //#define CIPSTER_ASSERT(assertion) assert(assertion) 164 | 165 | #else // for "Release" builds 166 | 167 | #define CIPSTER_ASSERT(x) // nothing 168 | #endif // NDEBUG 169 | 170 | #else // no CIPSTER_WITH_TRACES 171 | #define LOG_TRACE(x) // nothing 172 | #define CIPSTER_ASSERT(x) // nothing 173 | 174 | #endif 175 | 176 | 177 | #endif //CIPSTER_USER_CONF_H_ 178 | -------------------------------------------------------------------------------- /examples/WINDOWS/sample_application/sampleapplication.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define DEMO_APP_INPUT_ASSEMBLY_NUM 100 // 0x064 12 | #define DEMO_APP_OUTPUT_ASSEMBLY_NUM 150 // 0x096 13 | #define DEMO_APP_CONFIG_ASSEMBLY_NUM 151 // 0x097 14 | #define DEMO_APP_HEARBEAT_INPUT_ONLY_ASSEMBLY_NUM 152 // 0x098 15 | #define DEMO_APP_HEARBEAT_LISTEN_ONLY_ASSEMBLY_NUM 153 // 0x099 16 | #define DEMO_APP_EXPLICT_ASSEMBLY_NUM 154 // 0x09A 17 | 18 | // global variables for demo application (4 assembly data fields) *********** 19 | 20 | uint8_t g_assembly_data064[128]; // Input 21 | uint8_t g_assembly_data096[128]; // Output 22 | uint8_t g_assembly_data097[64]; // Config 23 | uint8_t g_assembly_data09A[128]; // Explicit 24 | 25 | 26 | EipStatus ApplicationInitialization() 27 | { 28 | // create 3 assembly object instances 29 | // INPUT 30 | CreateAssemblyInstance( DEMO_APP_INPUT_ASSEMBLY_NUM, 31 | ByteBuf( g_assembly_data064, sizeof(g_assembly_data064) ) ); 32 | 33 | // OUTPUT 34 | CreateAssemblyInstance( DEMO_APP_OUTPUT_ASSEMBLY_NUM, 35 | ByteBuf( g_assembly_data096, sizeof(g_assembly_data096) ) ); 36 | 37 | // CONFIG 38 | CreateAssemblyInstance( DEMO_APP_CONFIG_ASSEMBLY_NUM, 39 | ByteBuf( g_assembly_data097, sizeof(g_assembly_data097) ) ); 40 | 41 | // Heart-beat output assembly for Input only connections 42 | CreateAssemblyInstance( DEMO_APP_HEARBEAT_INPUT_ONLY_ASSEMBLY_NUM, 43 | ByteBuf( 0, 0 ) ); 44 | 45 | // Heart-beat output assembly for Listen only connections 46 | CreateAssemblyInstance( DEMO_APP_HEARBEAT_LISTEN_ONLY_ASSEMBLY_NUM, 47 | ByteBuf( 0, 0 ) ); 48 | 49 | // assembly for explicit messaging 50 | CreateAssemblyInstance( DEMO_APP_EXPLICT_ASSEMBLY_NUM, 51 | ByteBuf( g_assembly_data09A, sizeof(g_assembly_data09A) ) ); 52 | 53 | ConfigureExclusiveOwnerConnectionPoint( 54 | DEMO_APP_OUTPUT_ASSEMBLY_NUM, 55 | DEMO_APP_INPUT_ASSEMBLY_NUM, 56 | DEMO_APP_CONFIG_ASSEMBLY_NUM ); 57 | 58 | // Reserve a connection instance that can connect without a config_path 59 | ConfigureExclusiveOwnerConnectionPoint( 60 | DEMO_APP_OUTPUT_ASSEMBLY_NUM, 61 | DEMO_APP_INPUT_ASSEMBLY_NUM, 62 | -1 ); // config path may be omitted 63 | 64 | ConfigureInputOnlyConnectionPoint( 65 | DEMO_APP_HEARBEAT_INPUT_ONLY_ASSEMBLY_NUM, 66 | DEMO_APP_INPUT_ASSEMBLY_NUM, 67 | DEMO_APP_CONFIG_ASSEMBLY_NUM ); 68 | 69 | ConfigureListenOnlyConnectionPoint( 70 | DEMO_APP_HEARBEAT_LISTEN_ONLY_ASSEMBLY_NUM, 71 | DEMO_APP_INPUT_ASSEMBLY_NUM, 72 | DEMO_APP_CONFIG_ASSEMBLY_NUM ); 73 | 74 | return kEipStatusOk; 75 | } 76 | 77 | 78 | void HandleApplication() 79 | { 80 | // check if application needs to trigger a connection 81 | } 82 | 83 | 84 | void NotifyIoConnectionEvent( CipConn* aConn, IoConnectionEvent aEvent ) 85 | { 86 | // maintain a correct output state according to the connection state 87 | // maintain a correct output state according to the connection state 88 | int consuming_id = aConn->ConsumingPath().GetInstanceOrConnPt(); 89 | int producing_id = aConn->ProducingPath().GetInstanceOrConnPt(); 90 | } 91 | 92 | 93 | EipStatus AfterAssemblyDataReceived( AssemblyInstance* aInstance, 94 | OpMode aMode, int aBytesReceivedCount ) 95 | { 96 | EipStatus status = kEipStatusOk; 97 | 98 | // handle the data received e.g., update outputs of the device 99 | switch( aInstance->Id() ) 100 | { 101 | case DEMO_APP_OUTPUT_ASSEMBLY_NUM: 102 | /* Data for the output assembly has been received. 103 | * Mirror it to the inputs */ 104 | memcpy( &g_assembly_data064[0], &g_assembly_data096[0], 105 | sizeof(g_assembly_data064) ); 106 | break; 107 | 108 | case DEMO_APP_EXPLICT_ASSEMBLY_NUM: 109 | /* do something interesting with the new data from 110 | * the explicit set-data-attribute message */ 111 | break; 112 | 113 | case DEMO_APP_CONFIG_ASSEMBLY_NUM: 114 | /* Add here code to handle configuration data and check if it is ok 115 | * The demo application does not handle config data. 116 | * However in order to pass the test we accept any data given. 117 | * EIP_ERROR 118 | */ 119 | status = kEipStatusOk; 120 | break; 121 | } 122 | 123 | return status; 124 | } 125 | 126 | 127 | bool BeforeAssemblyDataSend( AssemblyInstance* instance ) 128 | { 129 | // update data to be sent e.g., read inputs of the device 130 | /*In this sample app we mirror the data from out to inputs on data receive 131 | * therefore we need nothing to do here. Just return true to inform that 132 | * the data is new. 133 | */ 134 | 135 | if( instance->Id() == DEMO_APP_EXPLICT_ASSEMBLY_NUM ) 136 | { 137 | /* do something interesting with the existing data 138 | * for the explicit get-data-attribute message */ 139 | } 140 | 141 | return true; 142 | } 143 | 144 | 145 | EipStatus ResetDevice() 146 | { 147 | // add reset code here 148 | return kEipStatusOk; 149 | } 150 | 151 | 152 | EipStatus ResetDeviceToInitialConfiguration( bool also_reset_comm_params ) 153 | { 154 | // reset the parameters 155 | 156 | // then perform device reset 157 | 158 | return kEipStatusOk; 159 | } 160 | 161 | 162 | void RunIdleChanged( uint32_t run_idle_value ) 163 | { 164 | (void) run_idle_value; 165 | } 166 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | SOFTWARE DISTRIBUTION LICENSE FOR THE 2 | ETHERNET/IP(TM) COMMUNICATION STACK 3 | (ADAPTED BSD STYLE LICENSE) 4 | 5 | Copyright (c) 2009, Rockwell Automation, Inc. ALL RIGHTS RESERVED. 6 | Copyright (c) 2016, SoftPLC Corporation. 7 | 8 | EtherNet/IP is a trademark of ODVA, Inc. 9 | 10 | Redistribution of the Communications Stack Software for EtherNet/IP and use in 11 | source and binary forms, with or without modification, are permitted provided 12 | that the following conditions are met: 13 | 14 | Redistributions of source code must retain the above copyright and trademark 15 | notices, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | 18 | Redistributions in binary form must reproduce the above copyright notice, this 19 | list of conditions and the following disclaimer in the documentation and/or 20 | other materials provided with the distribution. 21 | 22 | Neither the name of Rockwell Automation, ODVA, nor the names of its 23 | contributors may be used to endorse or promote products derived from this 24 | software without specific prior written permission from the respective owners. 25 | 26 | The Communications Stack Software for EtherNet/IP, or any portion thereof, with 27 | or without modifications, may be incorporated into products for sale. However, 28 | the software does not, by itself, convey any right to make, have made, use, 29 | import, offer to sell, sell, lease, market, or otherwise distribute or dispose 30 | of any products that implement this software, which products might be covered 31 | by valid patents or copyrights of ODVA, Inc., its members or other licensors 32 | nor does this software result in any license to use the EtherNet/IP mark owned 33 | by ODVA. To make, have made, use, import, offer to sell, sell, lease, market, 34 | or otherwise distribute or dispose of any products that implement this software, 35 | and to use the EtherNet/IP mark, one must obtain the necessary license from 36 | ODVA through its Terms of Usage Agreement for the EtherNet/IP technology, 37 | available through the ODVA web site at www.odva.org. This license requirement 38 | applies equally (a) to devices that completely implement ODVA's Final 39 | Specification for EtherNet/IP (Network Devices), (b) to components of such 40 | Network Devices to the extent they implement portions of the Final 41 | Specification for EtherNet/IP, and (c) to enabling technology products, such as 42 | any other EtherNet/IP or other network protocol stack designed for use in 43 | Network Devices to the extent they implement portions of the Final 44 | Specification for EtherNet/IP. Persons or entities who are not already licensed 45 | for the EtherNet/IP technology must contact ODVA for a Terms of Usage Agreement. 46 | 47 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 48 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 49 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 50 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 51 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 52 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 53 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 54 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 55 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 56 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 | 58 | -------------------------------------------------------------------------------- /source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Default to CMAKE_BUILD_TYPE = Release unless overridden on command line 2 | # http://www.cmake.org/pipermail/cmake/2008-September/023808.html 3 | if( DEFINED CMAKE_BUILD_TYPE ) 4 | set( CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Set to either \"Release\" or \"Debug\"" ) 5 | else() 6 | set( CMAKE_BUILD_TYPE Release CACHE STRING "Set to either \"Release\" or \"Debug\"" ) 7 | endif() 8 | 9 | 10 | cmake_minimum_required( VERSION 2.8.3 ) 11 | 12 | 13 | project( CIPster ) 14 | 15 | #if( CMAKE_VERSION VERSION_GREATER 3.1 ) 16 | # cmake_policy(SET CMP0056 OLD) 17 | #endif() 18 | 19 | set( CIPster_VERSION_MAJOR 1 ) 20 | set( CIPster_VERSION_MINOR 0 ) 21 | 22 | option( BYTEBUFS_INLINE "Use inline byte_bufs, which is bigger code and maybe a little faster" NO ) 23 | 24 | set( USER_INCLUDE_DIR "" CACHE PATH "Location of user specific include file (cipster_user_conf.h)" ) 25 | 26 | if( USER_INCLUDE_DIR STREQUAL "" ) 27 | message( WARNING "Please set variable USER_INCLUDE_DIR to a directory containing a file named cipster_user_conf.h" ) 28 | endif() 29 | 30 | 31 | include_directories( ${USER_INCLUDE_DIR} ) 32 | 33 | set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/buildsupport ) 34 | 35 | include( ${CMAKE_MODULE_PATH}/CIPster.cmake ) 36 | 37 | include( ${CMAKE_MODULE_PATH}/CIPster_function_checks.cmake ) 38 | 39 | 40 | # Doxygen Output #===================================== 41 | 42 | find_package( Doxygen ) 43 | if( DOXYGEN_FOUND ) 44 | add_custom_target( doxygen-docs 45 | ${CMAKE_COMMAND} -E remove_directory doc/doxygen 46 | COMMAND ${DOXYGEN_EXECUTABLE} 47 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 48 | DEPENDS Doxyfile 49 | COMMENT "building doxygen docs into directory doc/doxygen/html" 50 | ) 51 | else() 52 | message( STATUS "WARNING - Doxygen not found - doxygen-docs (Source Docs) target not created" ) 53 | endif() 54 | 55 | if( CMAKE_VERSION VERSION_GREATER 3.1 ) 56 | find_package( Iconv ) 57 | else() 58 | set( CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY "-fvisibility=" ) 59 | set( CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden" ) 60 | endif() 61 | 62 | if( Iconv_FOUND ) 63 | if( NOT Iconv_IS_BUILT_IN ) 64 | include_directories( ${Iconv_INCLUDE_DIR} ) 65 | endif() 66 | # TODO put into a config.h generator 67 | add_definitions( -DHAVE_ICONV=1 ) 68 | else() 69 | message( STATUS "WARNING - libiconv not found. BufReader::get_STRING2() 70 | and BufWriter::put_STRING2() will not be full featured" ) 71 | endif() 72 | 73 | set( CIPster_TRACES OFF CACHE BOOL "Activate CIPster traces" ) 74 | if(CIPster_TRACES) 75 | createTraceLevelOptions() 76 | else() 77 | message( "CIPster_TRACES is OFF" ) 78 | endif() 79 | 80 | 81 | set( CIPster_TESTS OFF CACHE BOOL "Enable tests to be built" ) 82 | if( CIPster_TESTS ) 83 | enable_testing() 84 | enable_language( CXX ) 85 | set( CPPUTEST_HOME "" CACHE PATH "Path to CppUTest directory" ) 86 | include( ${CMAKE_MODULE_PATH}/CIPster_Tests.cmake ) 87 | add_subdirectory( tests ) 88 | endif() 89 | 90 | 91 | if( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 92 | 93 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -ggdb3 -DDEBUG" ) 94 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall -Wno-reorder -Wno-sign-compare" ) 95 | 96 | if( APPLE ) 97 | add_definitions( -D_DARWIN_C_SOURCE ) 98 | endif() 99 | if( NOT APPLE ) 100 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY}hidden" ) 101 | endif() 102 | if( NOT APPLE ) 103 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN}" ) 104 | endif() 105 | 106 | if( WIN32 ) 107 | # https://sourceforge.net/p/mingw-w64/bugs/295/ 108 | add_definitions( -D__USE_MINGW_ANSI_STDIO ) 109 | else() 110 | add_definitions( -fPIC ) # The static library might eventually be linked into a DSO 111 | 112 | # enable clock_gettime: 113 | add_definitions( -D_POSIX_C_SOURCE=199309L ) 114 | endif() 115 | 116 | endif() 117 | 118 | add_subdirectory( src ) 119 | -------------------------------------------------------------------------------- /source/buildsupport/CIPster.cmake: -------------------------------------------------------------------------------- 1 | 2 | function(cipster_add_definition) 3 | foreach(ARG ${ARGV}) 4 | set_property(GLOBAL APPEND PROPERTY CIPSTER_DEFINITION ${ARG}) 5 | endforeach(ARG) 6 | endfunction() 7 | 8 | 9 | # Adds common Include directories 10 | macro(cipster_common_includes) 11 | set( SRC_DIR "${PROJECT_SOURCE_DIR}/src" ) 12 | set( CIP_SRC_DIR "${SRC_DIR}/cip" ) 13 | set( ENET_ENCAP_SRC_DIR "${SRC_DIR}/enet_encap" ) 14 | set( UTILS_SRC_DIR "${SRC_DIR}/utils") 15 | 16 | include_directories( 17 | ${PROJECT_SOURCE_DIR} 18 | ${SRC_DIR} 19 | ${CIP_SRC_DIR} 20 | ${ENET_ENCAP_SRC_DIR} 21 | ${PORTS_SRC_DIR} 22 | ${UTILS_SRC_DIR} 23 | ${CIPster_CIP_OBJECTS_DIR} 24 | ) 25 | endmacro() 26 | 27 | 28 | macro(cipster_add_cip_object NAME DESCRIPTION) 29 | set(CIPster_CIP_OBJECT_${NAME} OFF CACHE BOOL "${DESCRIPTION}") 30 | 31 | foreach(dependencies ${ARGN}) 32 | if(NOT ${dependencies}) 33 | return() 34 | endif() 35 | endforeach() 36 | 37 | if( NOT CIPster_CIP_OBJECT_${NAME} ) 38 | return() 39 | endif() 40 | 41 | endmacro() 42 | 43 | 44 | # Creates options for trace level 45 | macro(createTraceLevelOptions) 46 | add_definitions( -DCIPSTER_WITH_TRACES ) 47 | set( TRACE_LEVEL 0 ) 48 | set( CIPster_TRACE_LEVEL_ERROR ON CACHE BOOL "Error trace level" ) 49 | set( CIPster_TRACE_LEVEL_WARNING ON CACHE BOOL "Warning trace level" ) 50 | set( CIPster_TRACE_LEVEL_STATE ON CACHE BOOL "State trace level" ) 51 | set( CIPster_TRACE_LEVEL_INFO ON CACHE BOOL "Info trace level" ) 52 | 53 | if(CIPster_TRACE_LEVEL_ERROR) 54 | math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 1" ) 55 | endif() 56 | 57 | if(CIPster_TRACE_LEVEL_WARNING) 58 | math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 2" ) 59 | endif() 60 | 61 | if(CIPster_TRACE_LEVEL_STATE) 62 | math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 4" ) 63 | endif() 64 | 65 | if(CIPster_TRACE_LEVEL_INFO) 66 | math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 8" ) 67 | endif() 68 | 69 | add_definitions(-DCIPSTER_TRACE_LEVEL=${TRACE_LEVEL}) 70 | endmacro() 71 | -------------------------------------------------------------------------------- /source/buildsupport/CIPster_function_checks.cmake: -------------------------------------------------------------------------------- 1 | ######################################## 2 | # Check if functions exist on platform # 3 | ######################################## 4 | 5 | include (CheckFunctionExists) 6 | 7 | check_function_exists( srand HAVE_SRAND ) 8 | check_function_exists( rand HAVE_RAND ) 9 | 10 | if( (NOT(HAVE_SRAND)) OR (NOT(HAVE_RAND)) ) 11 | 12 | endif( (NOT(HAVE_SRAND)) OR (NOT(HAVE_RAND)) ) 13 | -------------------------------------------------------------------------------- /source/buildsupport/FindIconv.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2 | # file Copyright.txt or https://cmake.org/licensing for details. 3 | 4 | #[=======================================================================[.rst: 5 | FindIconv 6 | --------- 7 | 8 | This module finds the ``iconv()`` POSIX.1 functions on the system. 9 | These functions might be provided in the regular C library or externally 10 | in the form of an additional library. 11 | 12 | The following variables are provided to indicate iconv support: 13 | 14 | .. variable:: Iconv_FOUND 15 | 16 | Variable indicating if the iconv support was found. 17 | 18 | .. variable:: Iconv_INCLUDE_DIRS 19 | 20 | The directories containing the iconv headers. 21 | 22 | .. variable:: Iconv_LIBRARIES 23 | 24 | The iconv libraries to be linked. 25 | 26 | .. variable:: Iconv_IS_BUILT_IN 27 | 28 | A variable indicating whether iconv support is stemming from the 29 | C library or not. Even if the C library provides `iconv()`, the presence of 30 | an external `libiconv` implementation might lead to this being false. 31 | 32 | Additionally, the following :prop_tgt:`IMPORTED` target is being provided: 33 | 34 | .. variable:: Iconv::Iconv 35 | 36 | Imported target for using iconv. 37 | 38 | The following cache variables may also be set: 39 | 40 | .. variable:: Iconv_INCLUDE_DIR 41 | 42 | The directory containing the iconv headers. 43 | 44 | .. variable:: Iconv_LIBRARY 45 | 46 | The iconv library (if not implicitly given in the C library). 47 | 48 | .. note:: 49 | On POSIX platforms, iconv might be part of the C library and the cache 50 | variables ``Iconv_INCLUDE_DIR`` and ``Iconv_LIBRARY`` might be empty. 51 | 52 | #]=======================================================================] 53 | 54 | include(CMakePushCheckState) 55 | if(CMAKE_C_COMPILER_LOADED) 56 | include(CheckCSourceCompiles) 57 | elseif(CMAKE_CXX_COMPILER_LOADED) 58 | include(CheckCXXSourceCompiles) 59 | else() 60 | # If neither C nor CXX are loaded, implicit iconv makes no sense. 61 | set(Iconv_IS_BUILT_IN FALSE) 62 | endif() 63 | 64 | # iconv can only be provided in libc on a POSIX system. 65 | # If any cache variable is already set, we'll skip this test. 66 | if(NOT DEFINED Iconv_IS_BUILT_IN) 67 | if(UNIX AND NOT DEFINED Iconv_INCLUDE_DIR AND NOT DEFINED Iconv_LIBRARY) 68 | cmake_push_check_state(RESET) 69 | # We always suppress the message here: Otherwise on supported systems 70 | # not having iconv in their C library (e.g. those using libiconv) 71 | # would always display a confusing "Looking for iconv - not found" message 72 | set(CMAKE_FIND_QUIETLY TRUE) 73 | # The following code will not work, but it's sufficient to see if it compiles. 74 | # Note: libiconv will define the iconv functions as macros, so CheckSymbolExists 75 | # will not yield correct results. 76 | set(Iconv_IMPLICIT_TEST_CODE 77 | " 78 | #include 79 | #include 80 | int main() { 81 | char *a, *b; 82 | size_t i, j; 83 | iconv_t ic; 84 | ic = iconv_open(\"to\", \"from\"); 85 | iconv(ic, &a, &i, &b, &j); 86 | iconv_close(ic); 87 | } 88 | " 89 | ) 90 | if(CMAKE_C_COMPILER_LOADED) 91 | check_c_source_compiles("${Iconv_IMPLICIT_TEST_CODE}" Iconv_IS_BUILT_IN) 92 | else() 93 | check_cxx_source_compiles("${Iconv_IMPLICIT_TEST_CODE}" Iconv_IS_BUILT_IN) 94 | endif() 95 | cmake_pop_check_state() 96 | else() 97 | set(Iconv_IS_BUILT_IN FALSE) 98 | endif() 99 | endif() 100 | 101 | if(NOT Iconv_IS_BUILT_IN) 102 | find_path(Iconv_INCLUDE_DIR 103 | NAMES "iconv.h" 104 | DOC "iconv include directory") 105 | set(Iconv_LIBRARY_NAMES "iconv" "libiconv") 106 | else() 107 | set(Iconv_INCLUDE_DIR "" CACHE FILEPATH "iconv include directory") 108 | set(Iconv_LIBRARY_NAMES "c") 109 | endif() 110 | 111 | find_library(Iconv_LIBRARY 112 | NAMES ${Iconv_LIBRARY_NAMES} 113 | DOC "iconv library (potentially the C library)") 114 | 115 | mark_as_advanced(Iconv_INCLUDE_DIR) 116 | mark_as_advanced(Iconv_LIBRARY) 117 | 118 | include(FindPackageHandleStandardArgs) 119 | if(NOT Iconv_IS_BUILT_IN) 120 | find_package_handle_standard_args(Iconv REQUIRED_VARS Iconv_LIBRARY Iconv_INCLUDE_DIR) 121 | else() 122 | find_package_handle_standard_args(Iconv REQUIRED_VARS Iconv_LIBRARY) 123 | endif() 124 | 125 | if(Iconv_FOUND) 126 | set(Iconv_INCLUDE_DIRS "${Iconv_INCLUDE_DIR}") 127 | set(Iconv_LIBRARIES "${Iconv_LIBRARY}") 128 | if(NOT TARGET Iconv::Iconv) 129 | add_library(Iconv::Iconv INTERFACE IMPORTED) 130 | endif() 131 | set_property(TARGET Iconv::Iconv PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${Iconv_INCLUDE_DIRS}") 132 | set_property(TARGET Iconv::Iconv PROPERTY INTERFACE_LINK_LIBRARIES "${Iconv_LIBRARIES}") 133 | endif() 134 | -------------------------------------------------------------------------------- /source/buildsupport/OpENer_Tests.cmake: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Adds test includes # 3 | ####################################### 4 | macro( add_test_includes ) 5 | set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorNewMacros.h" ) 6 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorMallocMacros.h" ) 7 | include_directories( ${CPPUTEST_HOME}/include ) 8 | endmacro( add_test_includes ) 9 | -------------------------------------------------------------------------------- /source/buildsupport/Toolchain/Toolchain-EABI-ARM-Generic.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(CMakeForceCompiler) 2 | 3 | # Embedded System - No OS 4 | SET(CMAKE_SYSTEM_NAME Generic) 5 | # Specifiy CPU 6 | set(CMAKE_SYSTEM_PROCESSOR cortex-m3) 7 | 8 | # specify the cross compiler 9 | CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU) 10 | CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU) 11 | 12 | # Find the target environment prefix.. 13 | # First see where gcc is keeping libc.a 14 | execute_process( 15 | COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a 16 | OUTPUT_VARIABLE CMAKE_INSTALL_PREFIX 17 | OUTPUT_STRIP_TRAILING_WHITESPACE 18 | ) 19 | 20 | # Strip the filename off 21 | get_filename_component(CMAKE_INSTALL_PREFIX 22 | "${CMAKE_INSTALL_PREFIX}" PATH 23 | ) 24 | 25 | # Then find the canonical path to the directory one up from there 26 | get_filename_component(CMAKE_INSTALL_PREFIX 27 | "${CMAKE_INSTALL_PREFIX}/.." REALPATH 28 | ) 29 | 30 | set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FILEPATH 31 | "Install path prefix, prepended onto install directories.") 32 | 33 | message(STATUS "Cross-compiling with the gcc-arm-embedded toolchain") 34 | message(STATUS "Toolchain prefix: ${CMAKE_INSTALL_PREFIX}") 35 | 36 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_INSTALL_PREFIX}) 37 | 38 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 39 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 40 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 41 | 42 | set(CMAKE_C_FLAGS 43 | "${CMAKE_C_FLAGS}" 44 | "-fno-common" 45 | "-Wstrict-prototypes -ffunction-sections -fdata-sections" 46 | ) 47 | 48 | if (CMAKE_SYSTEM_PROCESSOR STREQUAL "cortex-m3") 49 | 50 | set(CMAKE_C_FLAGS 51 | "${CMAKE_C_FLAGS}" 52 | "-mcpu=cortex-m3 -march=armv7-m -mthumb" 53 | "-msoft-float" 54 | "-ffunction-sections -fdata-sections" 55 | ) 56 | endif() 57 | 58 | if (NOT DEFINED CMAKE_SYSTEM_PROCESSOR) 59 | message(WARNING 60 | "Processor not recognised in toolchain file, " 61 | "compiler flags not configured." 62 | ) 63 | endif () 64 | 65 | # When we break up long strings in CMake we get semicolon 66 | # separated lists, undo this here... 67 | string(REGEX REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 68 | 69 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "") 70 | 71 | set(BUILD_SHARED_LIBS OFF) 72 | -------------------------------------------------------------------------------- /source/buildsupport/Toolchain/toolchain-mingw64.cmake: -------------------------------------------------------------------------------- 1 | 2 | # CMake toolchain file for MinGW 64 bit 3 | # the name of the target operating system 4 | set( CMAKE_SYSTEM_NAME Windows ) 5 | 6 | # which compilers to use for C and C++ 7 | set( CMAKE_C_COMPILER x86_64-w64-mingw32-gcc ) 8 | set( CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++ ) 9 | set( CMAKE_RC_COMPILER x86_64-w64-mingw32-windres ) 10 | 11 | # here is the target environment located 12 | set( CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32 ) 13 | 14 | # adjust the default behaviour of the FIND_XXX() commands: 15 | # search headers and libraries in the target environment, search 16 | # programs in the host environment 17 | set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) 18 | set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) 19 | set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) 20 | 21 | # compiler defines: 22 | # __MINGW32__ is defined for both mingw32 and for mingw64. 23 | # For mingw64, __MINGW64__ is defined, too, when targetting 64 bits. 24 | 25 | option( MINGW64 "64 bit mingw toolchain" ON ) 26 | -------------------------------------------------------------------------------- /source/doc/coding_rules/rules.txt: -------------------------------------------------------------------------------- 1 | With the advent of CIPster being created, the project will be maintained using the 2 | coding rules in the KiCad project with an exception that class names will be 3 | CamelCase. 4 | 5 | https://git.launchpad.net/kicad/tree/Documentation/development/coding-style-policy.md 6 | -------------------------------------------------------------------------------- /source/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cipster_common_includes() 3 | 4 | 5 | set( CIP_SRCS 6 | cip/appcontype.cc 7 | cip/cipattribute.cc 8 | cip/cipinstance.cc 9 | cip/cipclass.cc 10 | cip/cipassembly.cc 11 | cip/cipcommon.cc 12 | cip/cipconnection.cc 13 | cip/cipconnectionmanager.cc 14 | cip/cipepath.cc 15 | cip/ciperror.cc 16 | cip/cipethernetlink.cc 17 | cip/cipidentity.cc 18 | cip/cipmessagerouter.cc 19 | cip/ciptcpipinterface.cc 20 | cip/cipvendors.cc 21 | ) 22 | 23 | set( ENCAP_SRCS 24 | enet_encap/cpf.cc 25 | enet_encap/encap.cc 26 | enet_encap/networkhandler.cc 27 | enet_encap/sockaddr.cc 28 | enet_encap/byte_bufs.cc 29 | ) 30 | 31 | if( BYTEBUFS_INLINE ) 32 | add_definitions( -DBYTEBUFS_INLINE=1 ) 33 | message( STATUS "Using inline byte_bufs" ) 34 | else() 35 | add_definitions( -DBYTEBUFS_INLINE=0 ) 36 | endif() 37 | 38 | set( UTILS_SRCS 39 | utils/random.cc 40 | utils/xorshiftrandom.cc 41 | utils/strprint.cc 42 | ) 43 | 44 | 45 | add_library( eip STATIC 46 | g_data.cc 47 | ${CIPster_ADD_CIP_OBJECTS} 48 | ${CIP_SRCS} 49 | ${ENCAP_SRCS} 50 | ${UTILS_SRCS} 51 | ) 52 | 53 | add_executable( test_byte_bufs EXCLUDE_FROM_ALL 54 | enet_encap/byte_bufs.cc 55 | ) 56 | set_target_properties( test_byte_bufs PROPERTIES 57 | COMPILE_FLAGS -DTEST_BYTE_BUFS 58 | ) 59 | target_link_libraries( test_byte_bufs 60 | eip 61 | ) 62 | 63 | message( "CMAKE_INSTALL_PREFIX:${CMAKE_INSTALL_PREFIX}" ) 64 | 65 | install( TARGETS eip DESTINATION . ) 66 | -------------------------------------------------------------------------------- /source/src/byte_bufs.impl: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016-2023, SoftPLC Corportion. 3 | * 4 | ******************************************************************************/ 5 | 6 | #include 7 | 8 | #if BYTEBUFS_INLINE 9 | # define MAYBE_INLINE inline 10 | #else 11 | # define MAYBE_INLINE /* nothing, so therefore not inline */ 12 | #endif 13 | 14 | MAYBE_INLINE BufWriter& BufWriter::operator+=( size_t advance ) 15 | { 16 | if( start + advance > limit ) 17 | overrun(); 18 | 19 | start += advance; 20 | return *this; 21 | } 22 | 23 | MAYBE_INLINE BufWriter BufWriter::operator+( size_t n ) 24 | { 25 | BufWriter ret = *this; 26 | 27 | ret += n; 28 | return ret; 29 | } 30 | 31 | MAYBE_INLINE uint8_t& BufWriter::operator * () 32 | { 33 | if( start >= limit ) 34 | overrun(); 35 | 36 | return *start; 37 | } 38 | 39 | MAYBE_INLINE BufWriter& BufWriter::operator++() // prefix ++ 40 | { 41 | if( start + 1 > limit ) 42 | overrun(); 43 | 44 | ++start; 45 | return *this; 46 | } 47 | 48 | 49 | MAYBE_INLINE BufWriter BufWriter::operator++(int) // postfix ++ 50 | { 51 | BufWriter result( *this ); 52 | 53 | if( start + 1 > limit ) 54 | overrun(); 55 | 56 | ++start; 57 | return result; 58 | } 59 | 60 | 61 | MAYBE_INLINE BufWriter& BufWriter::put8( uint8_t aValue ) 62 | { 63 | if( start + 1 > limit ) 64 | overrun(); 65 | 66 | *start++ = aValue; 67 | return *this; 68 | } 69 | 70 | MAYBE_INLINE BufWriter& BufWriter::put16( uint16_t aValue ) 71 | { 72 | if( start + 2 > limit ) 73 | overrun(); 74 | start[0] = (uint8_t) (aValue >> 0); 75 | start[1] = (uint8_t) (aValue >> 8); 76 | start += 2; 77 | return *this; 78 | } 79 | 80 | MAYBE_INLINE BufWriter& BufWriter::put32( uint32_t aValue ) 81 | { 82 | if( start + 4 > limit ) 83 | overrun(); 84 | start[0] = (uint8_t) (aValue >> 0); 85 | start[1] = (uint8_t) (aValue >> 8); 86 | start[2] = (uint8_t) (aValue >> 16); 87 | start[3] = (uint8_t) (aValue >> 24); 88 | start += 4; 89 | return *this; 90 | } 91 | 92 | MAYBE_INLINE BufWriter& BufWriter::put64( uint64_t aValue ) 93 | { 94 | if( start + 8 > limit ) 95 | overrun(); 96 | start[0] = (uint8_t) (aValue >> 0); 97 | start[1] = (uint8_t) (aValue >> 8); 98 | start[2] = (uint8_t) (aValue >> 16); 99 | start[3] = (uint8_t) (aValue >> 24); 100 | start[4] = (uint8_t) (aValue >> 32); 101 | start[5] = (uint8_t) (aValue >> 40); 102 | start[6] = (uint8_t) (aValue >> 48); 103 | start[7] = (uint8_t) (aValue >> 56); 104 | start += 8; 105 | return *this; 106 | } 107 | 108 | MAYBE_INLINE BufWriter& BufWriter::put_float( float aValue ) 109 | { 110 | union { 111 | float f; 112 | uint32_t i; 113 | } t; 114 | 115 | t.f = aValue; 116 | put32( t.i ); 117 | return *this; 118 | } 119 | 120 | MAYBE_INLINE BufWriter& BufWriter::put_double( double aValue ) 121 | { 122 | union { 123 | double d; 124 | uint64_t i; 125 | } t; 126 | 127 | t.d = aValue; 128 | put64( t.i ); 129 | return *this; 130 | } 131 | 132 | 133 | MAYBE_INLINE BufWriter& BufWriter::put16BE( uint16_t aValue ) 134 | { 135 | if( start + 2 > limit ) 136 | overrun(); 137 | start[1] = (uint8_t) (aValue >> 0); 138 | start[0] = (uint8_t) (aValue >> 8); 139 | start += 2; 140 | return *this; 141 | } 142 | 143 | 144 | MAYBE_INLINE BufWriter& BufWriter::put32BE( uint32_t aValue ) 145 | { 146 | if( start + 4 > limit ) 147 | overrun(); 148 | start[3] = (uint8_t) (aValue >> 0); 149 | start[2] = (uint8_t) (aValue >> 8); 150 | start[1] = (uint8_t) (aValue >> 16); 151 | start[0] = (uint8_t) (aValue >> 24); 152 | start += 4; 153 | return *this; 154 | } 155 | 156 | 157 | MAYBE_INLINE BufWriter& BufWriter::append( const uint8_t* aStart, size_t aCount ) 158 | { 159 | if( start + aCount > limit ) 160 | overrun(); 161 | memcpy( start, aStart, aCount ); 162 | start += aCount; 163 | return *this; 164 | } 165 | 166 | 167 | MAYBE_INLINE BufWriter& BufWriter::fill( size_t aCount, uint8_t aValue ) 168 | { 169 | if( start + aCount > limit ) 170 | overrun(); 171 | memset( start, aValue, aCount ); 172 | start += aCount; 173 | return *this; 174 | } 175 | 176 | 177 | //------------------------------------------------------------------- 178 | 179 | MAYBE_INLINE BufReader& BufReader::operator += ( size_t advance ) 180 | { 181 | if( start + advance > limit ) 182 | overrun(); 183 | 184 | start += advance; 185 | return *this; 186 | } 187 | 188 | 189 | MAYBE_INLINE BufReader BufReader::operator+( size_t n ) 190 | { 191 | BufReader ret = *this; 192 | ret += n; 193 | return ret; 194 | } 195 | 196 | 197 | MAYBE_INLINE BufReader& BufReader::operator++() // prefix ++ 198 | { 199 | return *this += 1; 200 | } 201 | 202 | 203 | MAYBE_INLINE BufReader BufReader::operator++(int) // postfix ++ 204 | { 205 | BufReader result( *this ); 206 | 207 | if( start + 1 > limit ) 208 | overrun(); 209 | 210 | ++start; 211 | return result; 212 | } 213 | 214 | 215 | MAYBE_INLINE uint8_t BufReader::operator * () const 216 | { 217 | if( start >= limit ) 218 | overrun(); 219 | return *start; 220 | } 221 | 222 | 223 | MAYBE_INLINE uint8_t BufReader::operator[] (int aIndex) const 224 | { 225 | if( start + (unsigned) aIndex >= limit ) 226 | overrun(); 227 | return start[aIndex]; 228 | } 229 | 230 | MAYBE_INLINE uint8_t BufReader::get8() 231 | { 232 | if( start + 1 > limit ) 233 | overrun(); 234 | return *start++; 235 | } 236 | 237 | 238 | MAYBE_INLINE uint16_t BufReader::get16() 239 | { 240 | if( start + 2 > limit ) 241 | overrun(); 242 | 243 | uint16_t ret = (start[0] << 0) | 244 | (start[1] << 8); 245 | start += 2; 246 | return ret; 247 | } 248 | 249 | 250 | MAYBE_INLINE uint32_t BufReader::get32() 251 | { 252 | if( start + 4 > limit ) 253 | overrun(); 254 | 255 | uint32_t ret = (start[0] << 0 ) | 256 | (start[1] << 8 ) | 257 | (start[2] << 16) | 258 | (start[3] << 24) ; 259 | start += 4; 260 | return ret; 261 | } 262 | 263 | 264 | MAYBE_INLINE uint64_t BufReader::get64() 265 | { 266 | if( start + 8 > limit ) 267 | overrun(); 268 | 269 | uint64_t ret = ((uint64_t) start[0] << 0 ) | 270 | ((uint64_t) start[1] << 8 ) | 271 | ((uint64_t) start[2] << 16) | 272 | ((uint64_t) start[3] << 24) | 273 | ((uint64_t) start[4] << 32) | 274 | ((uint64_t) start[5] << 40) | 275 | ((uint64_t) start[6] << 48) | 276 | ((uint64_t) start[7] << 56) ; 277 | start += 8; 278 | return ret; 279 | } 280 | 281 | 282 | MAYBE_INLINE float BufReader::get_float() 283 | { 284 | union { 285 | float f; 286 | uint32_t i; 287 | } t; 288 | 289 | t.i = get32(); 290 | 291 | return t.f; 292 | } 293 | 294 | 295 | MAYBE_INLINE double BufReader::get_double() 296 | { 297 | union { 298 | double d; 299 | uint64_t i; 300 | } t; 301 | 302 | t.i = get64(); 303 | return t.d; 304 | } 305 | 306 | 307 | MAYBE_INLINE uint16_t BufReader::get16BE() 308 | { 309 | if( start + 2 > limit ) 310 | overrun(); 311 | 312 | uint16_t ret = (start[1] << 0) | 313 | (start[0] << 8); 314 | start += 2; 315 | return ret; 316 | } 317 | 318 | 319 | MAYBE_INLINE uint32_t BufReader::get32BE() 320 | { 321 | if( start + 4 > limit ) 322 | overrun(); 323 | 324 | uint32_t ret = (start[3] << 0 ) | 325 | (start[2] << 8 ) | 326 | (start[1] << 16) | 327 | (start[0] << 24) ; 328 | start += 4; 329 | return ret; 330 | } 331 | -------------------------------------------------------------------------------- /source/src/cip/appcontype.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (C) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_APPCONTYPE_H_ 7 | #define CIPSTER_APPCONTYPE_H_ 8 | 9 | #include "cipconnectionmanager.h" 10 | 11 | 12 | /** 13 | * Function GetIoConnectionForConnectionData 14 | * checks if for the given connection data received in a forward_open request 15 | * whether a suitable connection is available, because it was registered previously or 16 | * opened previously. 17 | * 18 | * If a suitable connection is found the connection data is transfered the 19 | * application connection type is set (i.e., EConnType). 20 | * 21 | * @param aParams holds the connection data describing the needed connection 22 | * @param extended_error is where to put an error 23 | * 24 | * @return CipConn* - 25 | * - on success: A pointer to the connection object already containing the connection 26 | * data given in @a aParams. 27 | * - on error: NULL 28 | */ 29 | CipConn* GetIoConnectionForConnectionData( ConnectionData* aParams, 30 | ConnMgrStatus* extended_error ); 31 | 32 | /** @brief Check if there exists already an exclusive owner or listen only connection 33 | * which produces the input assembly. 34 | * 35 | * @param input_point the Input point to be produced 36 | * @return if a connection could be found a pointer to this connection if not NULL 37 | */ 38 | CipConn* GetExistingProducerMulticastConnection( int input_point ); 39 | 40 | /** @brief check if there exists an producing multicast exclusive owner or 41 | * listen only connection that should produce the same input but is not in charge 42 | * of the connection. 43 | * 44 | * @param input_point the produced input 45 | * @return if a connection could be found the pointer to this connection 46 | * otherwise NULL. 47 | */ 48 | CipConn* GetNextNonControlMasterConnection( int input_point ); 49 | 50 | /** @brief Close all connection producing the same input and have the same type 51 | * (i.e., listen only or input only). 52 | * 53 | * @param input_point the input point 54 | * @param instance_type the connection application type 55 | */ 56 | void CloseAllConnectionsForInputWithSameType( int input_point, 57 | ConnInstanceType instance_type ); 58 | 59 | /**@ brief close all open connections. 60 | * 61 | * For I/O connections the sockets will be freed. The sockets for explicit 62 | * connections are handled by the encapsulation layer, and freed there. 63 | */ 64 | void CloseAllConnections(); 65 | 66 | /** @brief Check if there is an established connection that uses the same 67 | * config point. 68 | * 69 | * @param config_point The configuration point 70 | * @return true if connection was found, otherwise false 71 | */ 72 | bool ConnectionWithSameConfigPointExists( int config_point ); 73 | 74 | #endif // CIPSTER_APPCONTYPE_H_ 75 | -------------------------------------------------------------------------------- /source/src/cip/cipassembly.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | //#include //needed for memcpy 8 | 9 | #include "cipassembly.h" 10 | 11 | #include 12 | #include "cipconnectionmanager.h" 13 | 14 | #undef INSTANCE_CLASS 15 | #define INSTANCE_CLASS AssemblyInstance 16 | 17 | // getter and setter of type AssemblyFunc, specific to this CIP class called "Assembly" 18 | 19 | AssemblyInstance::AssemblyInstance( int aInstanceId, ByteBuf aBuffer ) : 20 | CipInstance( aInstanceId ), 21 | byte_array( aBuffer ) 22 | { 23 | } 24 | 25 | 26 | EipStatus AssemblyInstance::RecvData( CipConn* aConn, BufReader aBuffer ) 27 | { 28 | if( ( aConn->ConsumingNCP().IsFixed() && SizeBytes() != aBuffer.size()) || 29 | (!aConn->ConsumingNCP().IsFixed() && SizeBytes() < aBuffer.size()) ) 30 | { 31 | CIPSTER_TRACE_ERR( 32 | "%s: wrong data amount: %zd bytes arrived for assembly id: %d\n", 33 | __func__, aBuffer.size(), Id() ); 34 | return kEipStatusError; 35 | } 36 | 37 | memcpy( Buffer().data(), aBuffer.data(), aBuffer.size() ); 38 | 39 | // notify application that new data arrived 40 | return AfterAssemblyDataReceived( this, aConn->Mode(), aBuffer.size() ); 41 | } 42 | 43 | 44 | CipAssemblyClass::CipAssemblyClass() : 45 | CipClass( kCipAssemblyClass, 46 | "Assembly", 47 | MASK7( 1,2,3,4,5,6,7 ), // common class attributes mask 48 | 2 // class revision 49 | ) 50 | { 51 | // Attribute 3 is the byte array transfer of the assembly data itself 52 | AttributeInsert( _I, 3, get_assembly_data_attr, false, set_assembly_data_attr, 53 | memb_offs(byte_array), true, kCipByteArray); 54 | 55 | // Attribute 4: is no. of bytes in Attribute 3 56 | AttributeInsert( _I, 4, kCipByteArrayLength, memb_offs(byte_array), true, false ); 57 | } 58 | 59 | 60 | AssemblyInstance* CipAssemblyClass::CreateInstance( int aInstanceId, ByteBuf aBuffer ) 61 | { 62 | CipAssemblyClass* clazz = (CipAssemblyClass*) GetCipClass( kCipAssemblyClass ); 63 | 64 | CIPSTER_ASSERT( clazz ); // Stack startup should have called CipAssemblyInitialize() 65 | 66 | AssemblyInstance* i = new AssemblyInstance( aInstanceId, aBuffer ); 67 | 68 | if( !clazz->InstanceInsert( i ) ) 69 | { 70 | delete i; 71 | i = NULL; 72 | } 73 | else 74 | { 75 | CIPSTER_TRACE_INFO( "%s: created assembly aInstanceId %d\n", __func__, aInstanceId ); 76 | } 77 | 78 | return i; 79 | } 80 | 81 | 82 | CipError CipAssemblyClass::OpenConnection( ConnectionData* aConnData, 83 | Cpf* aCpf, ConnMgrStatus* aExtError ) 84 | { 85 | return CipConnectionClass::OpenIO( aConnData, aCpf, aExtError ); 86 | } 87 | 88 | 89 | EipStatus CipAssemblyClass::Init() 90 | { 91 | if( !GetCipClass( kCipAssemblyClass ) ) 92 | { 93 | CipClass* clazz = new CipAssemblyClass(); 94 | 95 | RegisterCipClass( clazz ); 96 | } 97 | 98 | return kEipStatusOk; 99 | } 100 | 101 | 102 | EipStatus CipAssemblyClass::get_assembly_data_attr( CipInstance* aInstance, CipAttribute* attr, 103 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ) 104 | { 105 | AssemblyInstance* assembly = static_cast( aInstance ); 106 | 107 | if( !assembly->byte_array.size() ) 108 | { 109 | // assembly has no length, my be for a heartbeat connection, nothing to do. 110 | } 111 | else 112 | { 113 | BeforeAssemblyDataSend( assembly ); 114 | 115 | return CipAttribute::GetAttrData( assembly, attr, request, response ); 116 | } 117 | 118 | return kEipStatusOkSend; 119 | } 120 | 121 | 122 | EipStatus CipAssemblyClass::set_assembly_data_attr( CipInstance* aInstance, CipAttribute* attr, 123 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ) 124 | { 125 | AssemblyInstance* assembly = static_cast( aInstance ); 126 | 127 | if( IsConnectedInputAssembly( assembly->Id() ) ) 128 | { 129 | CIPSTER_TRACE_WARN( "%s: received data for connected input assembly\n", __func__ ); 130 | response->SetGenStatus( kCipErrorAttributeNotSetable ); 131 | } 132 | else if( request->Data().size() < assembly->byte_array.size() ) 133 | { 134 | CIPSTER_TRACE_INFO( "%s: not enough data received.\n", __func__ ); 135 | response->SetGenStatus( kCipErrorNotEnoughData ); 136 | } 137 | else if( request->Data().size() > assembly->byte_array.size() ) 138 | { 139 | CIPSTER_TRACE_INFO( "%s: too much data received.\n", __func__ ); 140 | response->SetGenStatus( kCipErrorTooMuchData ); 141 | } 142 | else if( !assembly->byte_array.size() ) 143 | { 144 | // assembly data has no length, may be for heartbeat connection, nothing to do. 145 | } 146 | else 147 | { 148 | CIPSTER_TRACE_INFO( 149 | "%s: writing %d bytes to assembly_id: %d.\n", 150 | __func__, 151 | (int) request->Data().size(), 152 | assembly->Id() 153 | ); 154 | 155 | memcpy( assembly->byte_array.data(), request->Data().data(), 156 | assembly->byte_array.size() ); 157 | 158 | if( AfterAssemblyDataReceived( assembly, 159 | kOpModeUnknown, request->Data().size() ) != kEipStatusOk ) 160 | { 161 | // NOTE: the attribute's data has already been overwritten. 162 | // Application did not like it. 163 | response->SetGenStatus( kCipErrorInvalidAttributeValue ); 164 | } 165 | } 166 | 167 | return kEipStatusOkSend; 168 | } 169 | -------------------------------------------------------------------------------- /source/src/cip/cipassembly.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_CIPASSEMBLY_H_ 7 | #define CIPSTER_CIPASSEMBLY_H_ 8 | 9 | #include 10 | #include "ciptypes.h" 11 | #include "cipclass.h" 12 | 13 | 14 | /** 15 | * Class AssemblyInstance 16 | * is extended from CipInstance with an extra kCipByteArray at the end. 17 | * That byte array has no ownership of the low level array, which for an 18 | * assembly is owned by the application program and passed into 19 | * CreateAssemblyInstance(). 20 | */ 21 | class AssemblyInstance : public CipInstance 22 | { 23 | friend class CipAssemblyClass; 24 | public: 25 | AssemblyInstance( int aInstanceId, ByteBuf aBuf ); 26 | 27 | unsigned SizeBytes() const { return byte_array.size(); } 28 | const ByteBuf& Buffer() const { return byte_array; } 29 | 30 | /** 31 | * Function RecvData 32 | * notifies an AssemblyInstance that data has been received for it. 33 | * 34 | * The data will be copied into the assembly instance's attribute 3 and 35 | * the application will be informed with the AfterAssemblyDataReceived() function. 36 | * 37 | * @param aConn which connection did the io connection datagram come in on? 38 | * It has the scanner specific Header32Bit info in it. 39 | * 40 | * @param aInput the byte data received and its length 41 | * @return 42 | * - kEipStatusOk the received data was okay 43 | * - EIP_ERROR the received data was wrong 44 | */ 45 | EipStatus RecvData( CipConn* aConn, BufReader aInput ); 46 | 47 | protected: 48 | ByteBuf byte_array; 49 | }; 50 | 51 | 52 | // public functions 53 | 54 | 55 | class CipAssemblyClass : public CipClass 56 | { 57 | public: 58 | CipAssemblyClass(); 59 | 60 | CipError OpenConnection( ConnectionData* aConnData, 61 | Cpf* aCpf, ConnMgrStatus* aExtError ); // override 62 | 63 | static AssemblyInstance* CreateInstance( int aInstanceId, ByteBuf aBuffer ); 64 | 65 | 66 | /** 67 | * Function Init 68 | * sets up the Assembly Class with zero instances and sets up all services. 69 | * 70 | * @return EipStatus - kEipStatusOk if assembly object was successfully 71 | * created, otherwise kEipStatusError 72 | */ 73 | static EipStatus Init(); 74 | 75 | protected: 76 | 77 | static EipStatus get_assembly_data_attr( CipInstance* aInstance, CipAttribute* attr, 78 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ); 79 | 80 | static EipStatus set_assembly_data_attr( CipInstance* aInstance, CipAttribute* attr, 81 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ); 82 | }; 83 | 84 | #endif // CIPSTER_CIPASSEMBLY_H_ 85 | -------------------------------------------------------------------------------- /source/src/cip/cipattribute.cc: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * Copyright (c) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | CipAttribute::CipAttribute( 13 | int aAttributeId, 14 | CipDataType aType, 15 | AttributeFunc aGetter, 16 | AttributeFunc aSetter, 17 | uintptr_t aData, 18 | bool isGetableAll, 19 | bool isDataAnInstanceOffset 20 | ) : 21 | attribute_id( aAttributeId ), 22 | type( aType ), 23 | is_getable_all( isGetableAll ), 24 | where( aData ), 25 | owning_class( 0 ), 26 | getter( aGetter ), 27 | setter( aSetter ), 28 | is_offset_from_instance_start( isDataAnInstanceOffset ) 29 | { 30 | /* 31 | Is there a problem with one of the calls to CipClass::AttributeInsert()? 32 | Likely you want either: 33 | 1) an offset from instance start, in which case aData's upper bits should be 0. 34 | 2) a full address in aData with is_offset_from_instance_start == false. 35 | */ 36 | 37 | CIPSTER_ASSERT( ( is_offset_from_instance_start && !(0xffff0000 & aData)) 38 | || (!is_offset_from_instance_start && (0xffff0000 & aData)) ); 39 | 40 | CIPSTER_ASSERT( aAttributeId > 0 && aAttributeId <= 65535 ); 41 | } 42 | 43 | 44 | EipStatus CipAttribute::GetAttrData( CipInstance* aInstance, CipAttribute* attr, 45 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ) 46 | { 47 | BufWriter out = response->Writer(); 48 | 49 | response->SetWrittenSize( EncodeData( attr->Type(), aInstance->Data(attr), out ) ); 50 | 51 | return kEipStatusOkSend; 52 | } 53 | 54 | 55 | EipStatus CipAttribute::SetAttrData( CipInstance* aInstance, CipAttribute* attr, 56 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ) 57 | { 58 | BufReader in = request->Data(); 59 | 60 | int out_count = DecodeData( attr->Type(), aInstance->Data(attr), in ); 61 | 62 | if( out_count >= 0 ) 63 | return kEipStatusOkSend; 64 | else 65 | return kEipStatusError; 66 | } 67 | 68 | 69 | EipStatus CipAttribute::Get( CipInstance* aInstance, 70 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ) 71 | { 72 | if( !IsGetableSingle() ) 73 | { 74 | response->SetGenStatus( kCipErrorAttributeNotGettable ); 75 | return kEipStatusOkSend; 76 | } 77 | else if( request->Service() == kGetAttributeAll && !IsGetableAll() ) 78 | { 79 | response->SetGenStatus( kCipErrorAttributeNotGettable ); 80 | return kEipStatusOkSend; 81 | } 82 | else 83 | { 84 | CIPSTER_TRACE_INFO( 85 | "%s: attribute:%d class:'%s' instance:%d\n", 86 | __func__, 87 | request->Path().GetAttribute(), 88 | aInstance->Class()->ClassName().c_str(), 89 | aInstance->Id() 90 | ); 91 | 92 | #if USE_MEMBER_FUNC_FOR_ATTRIBUTE_FUNC 93 | EipStatus ret = (aInstance->*getter)( this, request, response ); 94 | #else 95 | EipStatus ret = getter( aInstance, this, request, response ); 96 | #endif 97 | 98 | CIPSTER_TRACE_INFO( "%s: attribute_id:%d len:%u\n", 99 | __func__, Id(), response->WrittenSize() ); 100 | 101 | return ret; 102 | } 103 | } 104 | 105 | 106 | EipStatus CipAttribute::Set( CipInstance* aInstance, 107 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ) 108 | { 109 | if( !IsSetableSingle() ) 110 | { 111 | // it is an attribute we have, however this attribute is not setable 112 | response->SetGenStatus( kCipErrorAttributeNotSetable ); 113 | return kEipStatusOkSend; 114 | } 115 | else 116 | { 117 | return setter( aInstance, this, request, response ); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /source/src/cip/cipattribute.h: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * Copyright (c) 2009, Rockwell Automation, Inc. 4 | * Copyright (C) 2016-2018, SoftPLC Corporation. 5 | * 6 | ******************************************************************************/ 7 | 8 | 9 | #ifndef CIPATTRIBUTE_H_ 10 | #define CIPATTRIBUTE_H_ 11 | 12 | #define USE_MEMBER_FUNC_FOR_ATTRIBUTE_FUNC 0 13 | 14 | #include "ciptypes.h" 15 | 16 | // return a uint16_t to ensure that this fires the correct overload of 17 | // CipClass::AttributeInsert(), namely one that will set 18 | // CipAttribute::is_offset_from_instance_start = true before returning. 19 | // Will have to redefine INSTANCE_CLASS as explained below. 20 | // The '1' trick quiets older noisy compilers. 21 | #define memb_offs(Member) uint16_t( \ 22 | uintptr_t(&reinterpret_cast(1)->Member) - 1) 23 | 24 | /* 25 | For each class derived from CipInstance you may 26 | #undef INSTANCE_CLASS 27 | #define INSTANCE_CLASS DerivedInstance 28 | in the implementation C++ *.cc file. See cipassembly.cc for an example. 29 | */ 30 | #define INSTANCE_CLASS CipInstance 31 | 32 | class CipMessageRouterRequest; 33 | class CipMessageRouterResponse; 34 | class CipAttribute; 35 | class CipInstance; 36 | 37 | 38 | /** @ingroup CIP_API 39 | * @typedef EipStatus (*AttributeFunc)( CipAttribute *, 40 | * CipMessageRouterRequest*, CipMessageRouterResponse*) 41 | * 42 | * @brief Signature definition for the implementation of CIP services. 43 | * 44 | * @param aAttribute 45 | * 46 | * @param aRequest request data 47 | * 48 | * @param aResponse storage for the response data, including a buffer for 49 | * extended data. Do not advance aResponse->data BufWriter, but rather only set 50 | * aRequest->data_length to the number of bytes written to aReponse->data. 51 | * 52 | * @return kEipStatusOk_SEND if service could be executed successfully and a response 53 | * should be sent 54 | */ 55 | 56 | #if USE_MEMBER_FUNC_FOR_ATTRIBUTE_FUNC 57 | typedef EipStatus (CipInstance::*AttributeFunc) (CipAttribute* aAttribute, 58 | CipMessageRouterRequest* aRequest, 59 | CipMessageRouterResponse* aResponse); 60 | #else 61 | typedef EipStatus (*AttributeFunc)( CipInstance* aInstance, CipAttribute* aAttribute, 62 | CipMessageRouterRequest* aRequest, 63 | CipMessageRouterResponse* aResponse ); 64 | #endif 65 | 66 | /** 67 | * Class CipAttribute 68 | * holds info for a CIP attribute which may be ether: 69 | * 1) contained by a #CipInstance or 70 | * 2) a global or static variable. 71 | * If contained by an instance, then the @a where field is setup to hold an 72 | * offset from the start of the CipInstance base pointer, and this is 73 | * demarkated by setting @a is_offset_from_instance_start true. Otherwise 74 | * @a where holds a true pointer to the static or global variable which is not 75 | * an instance member of the CipInstance derivative. 76 | * 77 | * There is no final public accessor for "where", as this is done only by the 78 | * friend class CipInstance, via CipInstance::Data(CipAttribute*). 79 | */ 80 | class CipAttribute 81 | { 82 | friend class CipInstance; 83 | friend class CipClass; 84 | 85 | public: 86 | CipAttribute( 87 | int aAttributeId, 88 | CipDataType aType, 89 | AttributeFunc aGetter, 90 | AttributeFunc aSetter, 91 | uintptr_t aData, 92 | bool isGetableAll, 93 | bool isDataAnInstanceOffset = true 94 | ); 95 | 96 | //~CipAttribute() {} 97 | 98 | int Id() const { return attribute_id; } 99 | CipDataType Type() const { return type; } 100 | //void* Data() const { return data; } 101 | 102 | bool IsGetableSingle() { return getter != NULL; } 103 | bool IsSetableSingle() { return setter != NULL; } 104 | bool IsGetableAll() { return is_getable_all; } 105 | 106 | /** 107 | * Function Get 108 | * is called by GetAttributeSingle and GetAttributeAll services 109 | */ 110 | EipStatus Get( 111 | CipInstance* aInstance, 112 | CipMessageRouterRequest* aRequest, 113 | CipMessageRouterResponse* aResponse 114 | ); 115 | 116 | /** 117 | * Function Set 118 | * is called by SetAttributeSingle 119 | */ 120 | EipStatus Set( 121 | CipInstance* aInstance, 122 | CipMessageRouterRequest* aRequest, 123 | CipMessageRouterResponse* aResponse 124 | ); 125 | 126 | //---------------------------------------------------------- 127 | 128 | // Standard attribute getter functions, and you may add your own elsewhere also: 129 | static EipStatus GetAttrData( CipInstance* aInstance, CipAttribute* attr, 130 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ); 131 | 132 | // Standard attribute setter functions, and you may add your own elsewhere also: 133 | static EipStatus SetAttrData( CipInstance* aInstance, CipAttribute* attr, 134 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ); 135 | 136 | //--------------------------------------------------------- 137 | 138 | protected: 139 | 140 | int attribute_id; 141 | CipDataType type; 142 | bool is_getable_all; 143 | bool is_offset_from_instance_start; // or pointer to static or global 144 | uintptr_t where; 145 | CipClass* owning_class; 146 | 147 | /** 148 | * Function Pointer getter 149 | * may be fixed during construction to a custom getter function. 150 | */ 151 | const AttributeFunc getter; 152 | 153 | /** 154 | * Function Pointer setter 155 | * may be fixed during construction to a custom setter function. 156 | */ 157 | const AttributeFunc setter; 158 | }; 159 | 160 | typedef std::vector CipAttributes; 161 | 162 | #endif // CIPATTRIBUTE_H_ 163 | -------------------------------------------------------------------------------- /source/src/cip/cipcommon.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | 8 | #include "cipcommon.h" 9 | 10 | #include "trace.h" 11 | #include "cipster_api.h" 12 | #include "cipidentity.h" 13 | #include "ciptcpipinterface.h" 14 | #include "cipethernetlink.h" 15 | #include "cipconnectionmanager.h" 16 | #include "cipconnection.h" 17 | #include "byte_bufs.h" 18 | #include "encap.h" 19 | #include "ciperror.h" 20 | #include "cipassembly.h" 21 | #include "cipmessagerouter.h" 22 | #include "cpf.h" 23 | #include "appcontype.h" 24 | 25 | 26 | // global public variables 27 | uint8_t g_message_data_reply_buffer[CIPSTER_MESSAGE_DATA_REPLY_BUFFER]; 28 | 29 | // private functions 30 | 31 | void CipStackInit( uint16_t unique_connection_id ) 32 | { 33 | EipStatus eip_status; 34 | 35 | Encapsulation::Init(); 36 | 37 | // The message router is the first CIP object be initialized!!! 38 | eip_status = CipMessageRouterClass::Init(); 39 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 40 | 41 | eip_status = CipIdentityInit(); 42 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 43 | 44 | eip_status = CipTCPIPInterfaceClass::Init(); 45 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 46 | 47 | eip_status = CipEthernetLinkClass::Init(); 48 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 49 | 50 | eip_status = ConnectionManagerInit(); 51 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 52 | 53 | eip_status = CipConn::Init( unique_connection_id ); 54 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 55 | 56 | eip_status = CipAssemblyClass::Init(); 57 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 58 | 59 | #if 0 // do this in caller after return from this function. 60 | // the application has to be initialized last 61 | eip_status = ApplicationInitialization(); 62 | CIPSTER_ASSERT( kEipStatusOk == eip_status ); 63 | #endif 64 | 65 | (void) eip_status; 66 | } 67 | 68 | 69 | void ShutdownCipStack() 70 | { 71 | // First close all connections 72 | CloseAllConnections(); 73 | 74 | // Than free the sockets of currently active encapsulation sessions 75 | Encapsulation::ShutDown(); 76 | 77 | CipTCPIPInterfaceClass::Shutdown(); 78 | 79 | // destroy all the instances and classes 80 | CipClass::DeleteAll(); 81 | } 82 | 83 | 84 | int EncodeData( CipDataType aDataType, const void* input, BufWriter& aBuf ) 85 | { 86 | uint8_t* start = aBuf.data(); 87 | 88 | switch( aDataType ) 89 | { 90 | case kCipBool: 91 | case kCipSint: 92 | case kCipUsint: 93 | case kCipByte: 94 | aBuf.put8( *(uint8_t*) input ); 95 | break; 96 | 97 | case kCipInt: 98 | case kCipUint: 99 | case kCipWord: 100 | aBuf.put16( *(uint16_t*) input ); 101 | break; 102 | 103 | case kCipDint: 104 | case kCipUdint: 105 | case kCipDword: 106 | case kCipReal: 107 | aBuf.put32( *(uint32_t*) input ); 108 | break; 109 | 110 | case kCipLint: 111 | case kCipUlint: 112 | case kCipLword: 113 | case kCipLreal: 114 | aBuf.put64( *(uint64_t*) input ); 115 | break; 116 | 117 | case kCipStime: 118 | case kCipDate: 119 | case kCipTimeOfDay: 120 | case kCipDateAndTime: 121 | break; 122 | 123 | case kCipString: 124 | aBuf.put_STRING( * static_cast(input), false ); 125 | break; 126 | 127 | 128 | case kCipShortString: 129 | aBuf.put_SHORT_STRING( * static_cast(input), false ); 130 | break; 131 | 132 | case kCipString2: 133 | aBuf.put_STRING2( * static_cast(input) ); 134 | break; 135 | 136 | case kCipStringN: 137 | break; 138 | 139 | case kCipFtime: 140 | case kCipLtime: 141 | case kCipItime: 142 | case kCipTime: 143 | break; 144 | 145 | case kCipEngUnit: 146 | break; 147 | 148 | case kCipUsintUsint: 149 | { 150 | CipRevision* revision = (CipRevision*) input; 151 | 152 | aBuf.put8( revision->major_revision ); 153 | aBuf.put8( revision->minor_revision ); 154 | } 155 | break; 156 | 157 | case kCip6Usint: 158 | aBuf.append( (const uint8_t*) input, 6 ); 159 | break; 160 | 161 | case kCipMemberList: 162 | break; 163 | 164 | // The CipByteArray is implemented using a ByteBuf instance. 165 | case kCipByteArray: 166 | { 167 | BufReader rdr = *(ByteBuf*) input; 168 | 169 | aBuf.append( rdr ); 170 | } 171 | break; 172 | 173 | case kCipByteArrayLength: 174 | aBuf.put16( ((ByteBuf*) input)->size() ); 175 | break; 176 | 177 | default: 178 | break; 179 | } 180 | 181 | int byte_count = aBuf.data() - start; 182 | 183 | return byte_count; 184 | } 185 | 186 | 187 | int DecodeData( CipDataType aDataType, void* data, BufReader& aBuf ) 188 | { 189 | const uint8_t* start = aBuf.data(); 190 | 191 | switch( aDataType ) 192 | { 193 | case kCipBool: 194 | case kCipSint: 195 | case kCipUsint: 196 | case kCipByte: 197 | *(uint8_t*) data = aBuf.get8(); 198 | break; 199 | 200 | case kCipInt: 201 | case kCipUint: 202 | case kCipWord: 203 | *(uint16_t*) data = aBuf.get16(); 204 | break; 205 | 206 | case kCipDint: 207 | case kCipUdint: 208 | case kCipDword: 209 | *(uint32_t*) data = aBuf.get32(); 210 | break; 211 | 212 | case kCipLint: 213 | case kCipUlint: 214 | case kCipLword: 215 | *(uint64_t*) data = aBuf.get64(); 216 | break; 217 | 218 | // The CipByteArray is implemented using a ByteBuf instance. 219 | case kCipByteArray: 220 | { 221 | BufWriter w = *(ByteBuf*) data; 222 | 223 | w.append( aBuf ); 224 | aBuf += ((ByteBuf*)data)->size(); 225 | } 226 | break; 227 | 228 | case kCipByteArrayLength: 229 | { 230 | ByteBuf* bb = (ByteBuf*) data; 231 | 232 | *bb = ByteBuf( bb->data(), aBuf.get16() ); 233 | } 234 | break; 235 | 236 | case kCipString: 237 | *(std::string*) data = aBuf.get_STRING( true ); 238 | break; 239 | 240 | case kCipShortString: 241 | *(std::string*) data = aBuf.get_SHORT_STRING( true ); 242 | break; 243 | 244 | case kCipString2: 245 | *(std::string*) data = aBuf.get_STRING2(); 246 | break; 247 | 248 | default: 249 | return -1; 250 | } 251 | 252 | int byte_count = aBuf.data() - start; 253 | 254 | return byte_count; 255 | } 256 | 257 | -------------------------------------------------------------------------------- /source/src/cip/cipcommon.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_CIPCOMMON_H_ 7 | #define CIPSTER_CIPCOMMON_H_ 8 | 9 | /** @file cipcommon.h 10 | * Common CIP object interface 11 | */ 12 | 13 | #include 14 | #include "ciptypes.h" 15 | #include 16 | 17 | 18 | /// Binary search function template, dedicated for classes with Id() member func 19 | template< typename T, typename IterT > 20 | IterT vec_search( IterT begin, IterT end, T target ) 21 | { 22 | IterT initial_end = end; 23 | 24 | while( begin < end ) 25 | { 26 | IterT middle = begin + (end - begin - 1)/2; 27 | int r = target - (*middle)->Id(); 28 | 29 | if( r < 0 ) 30 | end = middle; 31 | else if( r > 0 ) 32 | begin = middle + 1; 33 | else 34 | return middle; 35 | } 36 | return initial_end; 37 | } 38 | 39 | 40 | /// Binary search function template, same as vec_search but returns equal or next greater 41 | template< typename T, typename IterT > 42 | IterT vec_search_gte( IterT begin, IterT end, T target ) 43 | { 44 | while( begin < end ) 45 | { 46 | IterT middle = begin + (end - begin - 1)/2; 47 | int r = target - (*middle)->Id(); 48 | 49 | if( r < 0 ) 50 | end = middle; 51 | else if( r > 0 ) 52 | begin = middle + 1; 53 | else 54 | return middle; 55 | } 56 | return end; 57 | } 58 | 59 | 60 | int StrPrintf( std::string* aResult, const char* aFormat, ... ); 61 | 62 | std::string StrPrintf( const char* aFormat, ... ); 63 | 64 | /** 65 | * Function CipVendor 66 | * return the Ethernet/IP vendor name given aVendorId. 67 | */ 68 | const char* CipVendorStr( int aVendorId ); 69 | 70 | 71 | /** A buffer for holding the reply generated by explicit message requests 72 | * or producing I/O connections. These will use this buffer in the following 73 | * ways: 74 | * 1. Explicit messages will use this buffer to store the data generated by the request 75 | * 2. I/O Connections will use this buffer for the produced data 76 | */ 77 | extern uint8_t g_message_data_reply_buffer[CIPSTER_MESSAGE_DATA_REPLY_BUFFER]; 78 | 79 | /// Set this to other than kEIP_IoUdpPort if your system receives consumes I/O 80 | /// messages on other than the standard port number. 81 | extern int g_my_io_udp_port; 82 | 83 | extern uint64_t g_current_usecs; 84 | 85 | /// The run/idle mode sent when our producing half is kRealTimeFmt32BitHeader, 86 | /// which is likely only when we are acting as a scanner. 87 | extern uint32_t g_run_idle_state; 88 | 89 | /// Return the least significant 32 bits of uint64_t g_current_usecs 90 | inline uint32_t CurrentUSecs32() { return uint32_t( g_current_usecs ); } 91 | 92 | 93 | #endif // CIPSTER_CIPCOMMON_H_ 94 | -------------------------------------------------------------------------------- /source/src/cip/cipconnectionmanager.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (C) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_CIPCONNECTIONMANAGER_H_ 7 | #define CIPSTER_CIPCONNECTIONMANAGER_H_ 8 | 9 | #include 10 | #include 11 | #include "ciptypes.h" 12 | #include "cipmessagerouter.h" 13 | #include "cipconnection.h" 14 | 15 | 16 | class CipConnMgrClass : public CipClass 17 | { 18 | public: 19 | CipConnMgrClass(); 20 | 21 | CipInstance* CreateInstance( int aInstanceId ); 22 | 23 | static EipStatus ManageConnections(); 24 | 25 | /** 26 | * Function CloseClass3Connections 27 | * closes all class 3 connections having @a aSessionHandle. 28 | * Since multiple CIP Class 3 connections can be instantiated all on the 29 | * same TCP connection, we could be deleting more than one here. 30 | * 31 | * @param aSessionHandle is a session handle to match against. 32 | */ 33 | static void CloseClass3Connections( CipUdint aSessionHandle ); 34 | 35 | static void CheckForTimedOutConnectionsAndCloseTCPConnections( CipUdint aSessionHandle ); 36 | 37 | /** 38 | * Function FindExsitingMatchingConnection 39 | * finds an existing matching established connection. 40 | * 41 | * The comparison is done according to the definitions in the CIP 42 | * specification Section 3-5.5.2: The following elements have to be equal: 43 | * Vendor ID, Connection Serial Number, Originator Serial Number 44 | * 45 | * @param aConn connection instance containing the comparison elements from 46 | * the forward open request 47 | * 48 | * @return CipConn* 49 | * - NULL if no equal established connection exists 50 | * - pointer to the equal connection object 51 | */ 52 | static CipConn* FindExistingMatchingConnection( const ConnectionData& params ); 53 | 54 | /** 55 | * Function RecvConnectedData 56 | * notifies the connection manager that data for a connection has been 57 | * received. 58 | * 59 | * This function is called by the networkhandler.cc code when a consumable 60 | * UDP frame has arrived on a socket known to be in use for an i/o connection. 61 | * 62 | * @param aSocket is the socket that the connected data arrived on. 63 | * @param aFromAddress address from which the data has been received. Only 64 | * data from the connections originator may be accepted. Avoids 65 | * connection hijacking 66 | * @param aCommand received data buffer pointing just past the 67 | * encapsulation header and a byte count remaining in frame. 68 | * @param aReply where to put the reply and tells its maximum length. 69 | * @return EipStatus 70 | */ 71 | static EipStatus RecvConnectedData( UdpSocket* aSocket, 72 | const SockAddr& aFromAddress, BufReader aCommand ); 73 | 74 | //----------------------------------------------------- 75 | static EipStatus forward_open_service( CipInstance* instance, 76 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ); 77 | 78 | static EipStatus large_forward_open_service( CipInstance* instance, 79 | CipMessageRouterRequest* request, CipMessageRouterResponse* response ); 80 | 81 | static EipStatus forward_close_service( CipInstance* instance, 82 | CipMessageRouterRequest* request, 83 | CipMessageRouterResponse* response ); 84 | 85 | //---------------------------------------------------- 86 | 87 | protected: 88 | 89 | /** 90 | * Function forward_open 91 | * is a client of both forward_open_service() and large_forward_open_service() 92 | * and checks if resources for new connection are available, and 93 | * generates a forward_open reply message. 94 | * 95 | * @param aInstance CIP object instance 96 | * @param aRequest CipMessageRouterRequest. 97 | * @param aResponse CipMessageRouterResponse. 98 | * @param isLarge is true when called from largeForwardOpen(), false when called from forwardOpen() 99 | * and the distinction is whether to expect 32 or 16 bits of "network connection parameters". 100 | * 101 | * @return EipStatus 102 | * - >0 .. success, 0 .. no reply to send back 103 | * - -1 .. error 104 | */ 105 | static EipStatus forward_open( CipInstance* aInstance, 106 | CipMessageRouterRequest* aRequest, 107 | CipMessageRouterResponse* aResponse, bool isLarge ); 108 | 109 | /** 110 | * Function assembleForwardOpenResponse 111 | * serializes a response to a forward_open 112 | static void assembleForwardOpenResponse( ConnectionData* aParams, 113 | CipMessageRouterResponse* response, CipError general_status, 114 | ConnMgrStatus extended_status ); 115 | */ 116 | }; 117 | 118 | 119 | /** 120 | * Macros for comparing 32 bit sequence numbers according to CIP spec Vol2 3-4.2. 121 | * @def SEQ_LEQ32(a, b) Checks if sequence number a is less or equal than b 122 | * @def SEQ_GEQ32(a, b) Checks if sequence number a is greater or equal than 123 | * @def SEQ_GT32(a, b) Checks if sequence number a is greater than b 124 | */ 125 | #define SEQ_LEQ32( a, b ) ( (int) ( (a) - (b) ) <= 0 ) 126 | #define SEQ_GEQ32( a, b ) ( (int) ( (a) - (b) ) >= 0 ) 127 | #define SEQ_GT32( a, b ) ( (int) ( (a) - (b) ) > 0 ) 128 | 129 | 130 | /** 131 | * Macros for comparing 16 bit sequence numbers. 132 | * @def SEQ_LEQ16(a, b) Checks if sequence number a is less or equal than b 133 | * @def SEQ_GEQ16(a, b) Checks if sequence number a is greater or equal than 134 | */ 135 | #define SEQ_LEQ16( a, b ) ( (short) ( (a) - (b) ) <= 0 ) 136 | #define SEQ_GEQ16( a, b ) ( (short) ( (a) - (b) ) >= 0 ) 137 | 138 | 139 | /** @brief Initialize the data of the connection manager object 140 | */ 141 | EipStatus ConnectionManagerInit(); 142 | 143 | /** 144 | * Function GetConnectionByConsumingId 145 | * returns a connection that has a matching consuming_connection id. 146 | * 147 | * @param aConnectionId is a consuming connection id to find. 148 | * @return CipConn* - the opened connection instance. 149 | * NULL .. open connection not present 150 | */ 151 | CipConn* GetConnectionByConsumingId( int aConnectionId ); 152 | 153 | /** 154 | * Function GetConnectionByProducingId 155 | * returns a connection that has a matching producing_connection id. 156 | * 157 | * @param aConnectionId is a producing connection id to find. 158 | * @return CipConn* - the opened connection instance. 159 | * NULL .. open connection not present 160 | */ 161 | CipConn* GetConnectionByProducingId( int aConnectionId ); 162 | 163 | /** Get a connection object for a given output assembly. 164 | * 165 | * @param output_assembly_id requested output assembly of requested 166 | * connection 167 | * @return pointer to connected Object 168 | * 0 .. connection not present in device 169 | */ 170 | CipConn* GetConnectedOutputAssembly( int output_assembly_id ); 171 | 172 | 173 | bool IsConnectedInputAssembly( int aInstanceId ); 174 | 175 | // TODO: Missing documentation 176 | bool IsConnectedOutputAssembly( int aInstanceId ); 177 | 178 | /** 179 | * Class CipConnBox 180 | * is a containter for CipConns (likely to be replace with std::vector some day). 181 | * Used to hold an active list of CipConns, using CipConn->prev and ->next. 182 | */ 183 | class CipConnBox 184 | { 185 | public: 186 | 187 | CipConnBox() : 188 | head( NULL ) 189 | {} 190 | 191 | /// Class CipConnBox::iterator walks the linked list and mimics a pointer 192 | /// when the dereferencing operators and cast are used. 193 | class iterator 194 | { 195 | public: 196 | iterator( CipConn* aConn ) : p( aConn ) {} 197 | 198 | iterator& operator ++() 199 | { 200 | p = p->next; 201 | return *this; 202 | } 203 | 204 | iterator operator ++( int ) // post-increment and return initial position 205 | { 206 | iterator ret( p ); 207 | p = p->next; 208 | return ret; 209 | } 210 | 211 | bool operator == ( const iterator& other ) const 212 | { 213 | return p == other.p; 214 | } 215 | 216 | bool operator != ( const iterator& other ) const 217 | { 218 | return p != other.p; 219 | } 220 | 221 | CipConn* operator->() const { return p; } 222 | CipConn& operator*() const { return *p; } 223 | operator CipConn* () const { return p; } 224 | 225 | private: 226 | CipConn* p; 227 | }; 228 | 229 | /** 230 | * Function Insert 231 | * inserts the given connection object into this container. 232 | * 233 | * By adding a connection to the active connection list the connection manager 234 | * will perform the supervision and handle the timing (e.g., timeout, 235 | * production inhibit, etc). 236 | * 237 | * @param aConn the connection to be added at the beginning. 238 | * @return bool - true if it was successfully inserted, else false because 239 | * aConn was already on the list. 240 | */ 241 | bool Insert( CipConn* aConn ); 242 | 243 | /** 244 | * Function Remove 245 | * @return bool - true if it was successfully removed, else false because 246 | * aConn was not previously on the list. 247 | */ 248 | bool Remove( CipConn* aConn ); 249 | 250 | iterator end() const { return iterator( NULL ); } 251 | iterator begin() const { return iterator( head ); } 252 | 253 | protected: 254 | CipConn* head; 255 | }; 256 | 257 | extern CipConnBox g_active_conns; 258 | 259 | #endif // CIPSTER_CIPCONNECTIONMANAGER_H_ 260 | -------------------------------------------------------------------------------- /source/src/cip/ciperror.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018, SoftPLC Corporation. 3 | * 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "ciperror.h" 8 | 9 | // This function is in its own file so that it can be omited during linking 10 | // if not referenced elsewhere. 11 | 12 | const char* ExtStatusStr( ConnMgrStatus aExtStatus ) 13 | { 14 | const char* rs = "?"; 15 | 16 | switch( aExtStatus ) 17 | { 18 | case kConnMgrStatusSuccess: rs = "success"; break; 19 | case kConnMgrStatusConnectionInUse: rs = "connection in use"; break; 20 | case kConnMgrStatusTransportTriggerNotSupported: rs = "transport trigger not supported"; break; 21 | case kConnMgrStatusOwnershipConflict: rs = "ownership conflict"; break; 22 | case kConnMgrStatusConnectionNotFoundAtTargetApplication: rs = "connection not found at target application"; break; 23 | case kConnMgrStatusInvalidNetworkConnectionParameter: rs = "invalid network connection parameter"; break; 24 | case kConnMgrStatusInvalidConnectionSize: rs = "invalid connection size"; break; 25 | case kConnMgrStatusRPINotSupported: rs = "RPI not supported"; break; 26 | case kConnMgrStatusRPIValuesNotAcceptable: rs = "RPI value not acceptable"; break; 27 | case kConnMgrStatusNoMoreConnectionsAvailable: rs = "no more connections available"; break; 28 | case kConnMgrStatusVendorIdOrProductcodeError: rs = "vendor id or product code error"; break; 29 | case kConnMgrStatusDeviceTypeError: rs = "device type error"; break; 30 | case kConnMgrStatusRevisionMismatch: rs = "revision mismatch"; break; 31 | case kConnMgrStatusNonListenOnlyConnectionNotOpened: rs = "non-listen only connection not opened"; break; 32 | case kConnMgrStatusTargetObjectOutOfConnections: rs = "target out of connections"; break; 33 | case kConnMgrStatusPITGreaterThanRPI: rs = "PIT greater than RPI"; break; 34 | case kConnMgrStatusInvalidOToTConnectionType: rs = "invalid O->T connection type"; break; 35 | case kConnMgrStatusInvalidTToOConnectionType: rs = "invalid T->O connection type"; break; 36 | case kConnMgrStatusInvalidOToTConnectionSize: rs = "invalid O->T connection size"; break; 37 | case kConnMgrStatusInvalidTToOConnectionSize: rs = "invalid T->O connection size"; break; 38 | case kConnMgrStatusInvalidConfigurationApplicationPath: rs = "invalid configuration app_path"; break; 39 | case kConnMgrStatusInvalidConsumingApllicationPath: rs = "invalid consuming app_path"; break; 40 | case kConnMgrStatusInvalidProducingApplicationPath: rs = "invalid producing app_path"; break; 41 | case kConnMgrStatusInconsistentApplicationPathCombo: rs = "inconsisten app_path combo"; break; 42 | case kConnMgrStatusNullForwardOpenFunctionNotSupported: rs = "null forward open function not supported"; break; 43 | case kConnMgrStatusConnectionTimeoutMultiplierNotAcceptable:rs = "connection timeout multiplier not acceptable"; break; 44 | case kConnMgrStatusParameterErrorInUnconnectedSendService: rs = "parameter error in unconnected send service"; break; 45 | case kConnMgrStatusInvalidSegmentTypeInPath: rs = "invalid segment type in path"; break; 46 | case kConnMgrStatusInForwardClosePathMismatch: rs = "forward close path mismatch"; break; 47 | 48 | // default: ; no, we want compiler warning when this switch is incomplete enumeration 49 | } 50 | 51 | return rs; 52 | } 53 | -------------------------------------------------------------------------------- /source/src/cip/cipethernetlink.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #undef INSTANCE_CLASS 18 | #define INSTANCE_CLASS CipEthernetLinkInstance 19 | 20 | 21 | CipEthernetLinkInstance* CipEthernetLinkClass::CreateInstance() 22 | { 23 | CipEthernetLinkInstance* i = new CipEthernetLinkInstance( Instances().size() + 1 ); 24 | InstanceInsert( i ); 25 | 26 | return i; 27 | } 28 | 29 | 30 | CipEthernetLinkClass::CipEthernetLinkClass() : 31 | CipClass( kCipEthernetLinkClass, 32 | "Ethernet Link", 33 | MASK7(1,2,3,4,5,6,7), // common class attributes mask 34 | 1 // version 35 | ) 36 | { 37 | AttributeInsert( _I, 1, kCipUdint, memb_offs(interface_speed) ); 38 | AttributeInsert( _I, 2, kCipDword, memb_offs(interface_flags) ); 39 | AttributeInsert( _I, 3, kCip6Usint, memb_offs(physical_address) ); 40 | } 41 | 42 | 43 | EipStatus CipEthernetLinkClass::Init() 44 | { 45 | if( !GetCipClass( kCipEthernetLinkClass ) ) 46 | { 47 | CipEthernetLinkClass* clazz = new CipEthernetLinkClass(); 48 | 49 | RegisterCipClass( clazz ); 50 | 51 | // create instance 1 52 | clazz->CreateInstance(); 53 | } 54 | 55 | return kEipStatusOk; 56 | } 57 | 58 | 59 | void CipEthernetLinkClass::ConfigureMacAddress( int aInstanceId, const uint8_t* mac_address ) 60 | { 61 | CipClass* clazz = GetCipClass( kCipEthernetLinkClass ); 62 | 63 | if( clazz ) 64 | { 65 | CipEthernetLinkInstance* i = static_cast( clazz->Instance( aInstanceId ) ); 66 | 67 | if( i ) 68 | { 69 | memcpy( &i->physical_address, mac_address, sizeof i->physical_address ); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /source/src/cip/cipethernetlink.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_CIPETHERNETLINK_H_ 7 | #define CIPSTER_CIPETHERNETLINK_H_ 8 | 9 | #include "typedefs.h" 10 | #include "ciptypes.h" 11 | #include "cipclass.h" 12 | 13 | 14 | /** 15 | * Class CipEthernetLinkInstance 16 | */ 17 | class CipEthernetLinkInstance : public CipInstance 18 | { 19 | friend class CipEthernetLinkClass; 20 | 21 | public: 22 | CipEthernetLinkInstance( int aInstanceId ) : 23 | CipInstance( aInstanceId ), 24 | interface_speed( 100 ), 25 | 26 | // successful speed and duplex neg, full duplex active link, 27 | // TODO in future it should be checked if link is active 28 | interface_flags( 0xf ), 29 | physical_address() 30 | {} 31 | 32 | 33 | protected: 34 | uint32_t interface_speed; 35 | uint32_t interface_flags; 36 | uint8_t physical_address[6]; 37 | }; 38 | 39 | 40 | /** 41 | * Class CipEthernetLinkClass 42 | */ 43 | class CipEthernetLinkClass : public CipClass 44 | { 45 | public: 46 | 47 | /** 48 | * Function Init 49 | * initializes the Ethernet Link Objects 50 | */ 51 | static EipStatus Init(); 52 | 53 | static void ConfigureMacAddress( int aInstanceId, const uint8_t* mac_address ); 54 | 55 | CipEthernetLinkClass(); 56 | 57 | CipEthernetLinkInstance* CreateInstance(); 58 | }; 59 | 60 | 61 | #endif // CIPSTER_CIPETHERNETLINK_H_ 62 | -------------------------------------------------------------------------------- /source/src/cip/cipidentity.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** 8 | * @file cipidentity.cc 9 | * 10 | * CIP Identity Object 11 | * =================== 12 | * 13 | * Implemented Attributes 14 | * ---------------------- 15 | * - Attribute 1: VendorID 16 | * - Attribute 2: Device Type 17 | * - Attribute 3: Product Code 18 | * - Attribute 4: Revision 19 | * - Attribute 5: Status 20 | * - Attribute 6: Serial Number 21 | * - Attribute 7: Product Name 22 | * 23 | * Implemented Services 24 | * -------------------- 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | 38 | 39 | // attributes in CIP Identity Object, some are public so they can be examined 40 | // when testing electronic key validity. 41 | 42 | uint16_t vendor_id_ = CIPSTER_DEVICE_VENDOR_ID; //*< Attribute 1: Vendor ID 43 | 44 | uint16_t device_type_ = CIPSTER_DEVICE_TYPE; //*< Attribute 2: Device Type 45 | 46 | uint16_t product_code_ = CIPSTER_DEVICE_PRODUCT_CODE; //*< Attribute 3: Product Code 47 | 48 | /// Attribute 4: Revision / USINT Major, USINT Minor 49 | CipRevision revision_ 50 | { 51 | CIPSTER_DEVICE_MAJOR_REVISION, 52 | CIPSTER_DEVICE_MINOR_REVISION 53 | }; 54 | 55 | uint16_t status_ = 0; //*< Attribute 5: Status 56 | 57 | uint32_t serial_number_ = 0; //*< Attribute 6: Serial Number, has to be set prior to CIPster initialization 58 | 59 | std::string product_name_ = CIPSTER_DEVICE_NAME; 60 | 61 | 62 | void SetDeviceSerialNumber( uint32_t serial_number ) 63 | { 64 | serial_number_ = serial_number; 65 | } 66 | 67 | 68 | void SetDeviceStatus( uint16_t status ) 69 | { 70 | status_ = status; 71 | } 72 | 73 | 74 | /** Reset service 75 | * 76 | * @param instance 77 | * @param request 78 | * @param response 79 | * @returns Currently always kEipOkSend is returned 80 | */ 81 | static EipStatus reset_service( CipInstance* instance, 82 | CipMessageRouterRequest* request, 83 | CipMessageRouterResponse* response ) 84 | { 85 | (void) instance; 86 | 87 | EipStatus eip_status = kEipStatusOkSend; 88 | 89 | if( request->Data().size() == 1 ) 90 | { 91 | int value = request->Data().data()[0]; 92 | 93 | CIPSTER_TRACE_INFO( "%s: request->data_length=%d value=%d\n", 94 | __func__, (int) request->Data().size(), value ); 95 | 96 | switch( value ) 97 | { 98 | case 0: // Reset type 0 -> emulate device reset / Power cycle 99 | if( kEipStatusOk == ResetDevice() ) 100 | { 101 | // in this case there is no response since I am rebooting. 102 | } 103 | response->SetGenStatus( kCipErrorDeviceStateConflict ); 104 | break; 105 | 106 | case 1: // Reset type 1 -> reset to device settings 107 | if( kEipStatusOk == ResetDeviceToInitialConfiguration( true ) ) 108 | { 109 | // in this case there is no response since I am rebooting. 110 | } 111 | response->SetGenStatus( kCipErrorDeviceStateConflict ); 112 | break; 113 | 114 | case 2: // Reset type 2 -> Return to factory defaults except communications parameters 115 | if( kEipStatusOk == ResetDeviceToInitialConfiguration( false ) ) 116 | { 117 | // in this case there is no response since I am rebooting. 118 | } 119 | response->SetGenStatus( kCipErrorDeviceStateConflict ); 120 | break; 121 | 122 | default: 123 | response->SetGenStatus( kCipErrorInvalidParameter ); 124 | break; 125 | } 126 | } 127 | else if( request->Data().size() == 0 ) 128 | { 129 | CIPSTER_TRACE_INFO( "%s: request->data_length=0\n", __func__ ); 130 | 131 | // Reset type 0 -> emulate device reset / Power cycle 132 | if( kEipStatusOk == ResetDevice() ) 133 | { 134 | // in this case there is no response since I am rebooting. 135 | } 136 | response->SetGenStatus( kCipErrorDeviceStateConflict ); 137 | } 138 | else 139 | { 140 | response->SetGenStatus( kCipErrorInvalidParameter ); 141 | } 142 | 143 | return eip_status; 144 | } 145 | 146 | 147 | static CipInstance* createIdentityInstance() 148 | { 149 | CipClass* clazz = GetCipClass( kCipIdentityClass ); 150 | 151 | CipInstance* i = new CipInstance( clazz->Instances().size() + 1 ); 152 | 153 | clazz->InstanceInsert( i ); 154 | 155 | return i; 156 | } 157 | 158 | 159 | class CipIdentityClass : public CipClass 160 | { 161 | public: 162 | CipIdentityClass(); 163 | }; 164 | 165 | 166 | CipIdentityClass::CipIdentityClass() : 167 | CipClass( 168 | kCipIdentityClass, 169 | "Identity", // class name 170 | // Vol1 5A-2.1 says class attributes 3 thru 7 are optional. 171 | MASK4( 1, 2, 6, 7 ), 172 | 173 | // 24-Jul-2018: conformance tool whines erroneously when we 174 | // report kCipErrorAttributeNotSupported for GetAttributeSingle 175 | // against attributes 3, 4, 5. I think this is their bug. 176 | 177 | 1 // class revision 178 | ) 179 | { 180 | // All attributes are read only, and the conformance tool wants error code 181 | // 0x08 not 0x14 when testing for SetAttributeSingle 182 | delete ServiceRemove( _I, kSetAttributeSingle ); 183 | 184 | ServiceInsert( _I, kReset, reset_service, "Reset" ); 185 | 186 | AttributeInsert( _I, 1, kCipUint, &vendor_id_ ); 187 | AttributeInsert( _I, 2, kCipUint, &device_type_ ); 188 | AttributeInsert( _I, 3, kCipUint, &product_code_ ); 189 | AttributeInsert( _I, 4, kCipUsintUsint, &revision_ ); 190 | AttributeInsert( _I, 5, kCipWord, &status_ ); 191 | AttributeInsert( _I, 6, kCipUdint, &serial_number_ ); 192 | AttributeInsert( _I, 7, kCipShortString, &product_name_ ); 193 | } 194 | 195 | 196 | EipStatus CipIdentityInit() 197 | { 198 | if( !GetCipClass( kCipIdentityClass ) ) 199 | { 200 | CipClass* clazz = new CipIdentityClass(); 201 | 202 | RegisterCipClass( clazz ); 203 | 204 | createIdentityInstance(); 205 | } 206 | 207 | return kEipStatusOk; 208 | } 209 | -------------------------------------------------------------------------------- /source/src/cip/cipidentity.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_CIPIDENTITY_H_ 7 | #define CIPSTER_CIPIDENTITY_H_ 8 | 9 | #include "typedefs.h" 10 | #include "ciptypes.h" 11 | 12 | /// Status of the CIP Identity object 13 | enum CipIdentityStatus 14 | { 15 | kOwned = 0x0001, ///< Indicates that the device has an owner 16 | kConfigured = 0x0004, /**< Indicates that the device is configured to do 17 | * something different, than the out-of-the-box default. */ 18 | kMinorRecoverableFault = 0x0100, /**< Indicates that the device detected a 19 | * fault with itself, which was thought to be recoverable. The device did not 20 | * switch to a faulted state. */ 21 | kMinorUncoverableFault = 0x0200, /**< Indicates that the device detected a 22 | * fault with itself, which was thought to be recoverable. The device did not 23 | * switch to a faulted state. */ 24 | kMajorRecoveralbeFault = 0x0400, /**< Indicates that the device detected a 25 | * fault with itself,which was thought to be recoverable. The device changed 26 | * to the "Major Recoverable Fault" state */ 27 | kMajorUnrecoverableFault = 0x0800 /**< Indicates that the device detected a 28 | * fault with itself,which was thought to be recoverable. The device changed 29 | * to the "Major Unrecoverable Fault" state */ 30 | }; 31 | 32 | 33 | enum CipIdentityExtendedStatus 34 | { 35 | kSelftestingUnknown = 0x0000, 36 | FirmwareUpdateInProgress = 0x0010, 37 | kStatusAtLeastOneFaultedIoConnection = 0x0020, 38 | kNoIoConnectionsEstablished = 0x0030, 39 | kNonVolatileConfigurationBad = 0x0040, 40 | kMajorFault = 0x0050, 41 | kAtLeastOneIoConnectionInRuneMode = 0x0060, 42 | kAtLeastOneIoConnectionEstablishedAllInIdleMode = 0x0070 43 | }; 44 | 45 | 46 | /** 47 | * Function CipIdentityInit 48 | * sets up the CIP Identity class and instance. 49 | * 50 | * @return EipStatus - EipError if failure, else EipOk 51 | */ 52 | EipStatus CipIdentityInit(); 53 | 54 | 55 | // Publics from the CIP identity object 56 | extern uint16_t vendor_id_; 57 | extern uint16_t device_type_; 58 | extern uint16_t product_code_; 59 | extern CipRevision revision_; 60 | extern uint16_t status_; 61 | extern uint32_t serial_number_; 62 | extern std::string product_name_; 63 | 64 | 65 | #endif // CIPSTER_CIPIDENTITY_H_ 66 | -------------------------------------------------------------------------------- /source/src/cip/cipinstance.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | CipInstance::CipInstance( int aInstanceId ) : 13 | instance_id( aInstanceId ), 14 | owning_class( 0 ) // NULL (not owned) until I am inserted into a CipClass 15 | { 16 | } 17 | 18 | 19 | CipInstance::~CipInstance() 20 | { 21 | if( instance_id ) // not a public class, then I am an instance. 22 | { 23 | if( owning_class ) // if I was inserted int a class, then this is non-NULL 24 | { 25 | CIPSTER_TRACE_INFO( "deleting instance %d of class '%s'\n", 26 | instance_id, owning_class->ClassName().c_str() ); 27 | } 28 | } 29 | } 30 | 31 | 32 | CipService* CipInstance::Service( int aServiceId ) const 33 | { 34 | CIPSTER_ASSERT( owning_class ); 35 | 36 | return owning_class->Service( CI_(), aServiceId ); 37 | } 38 | 39 | 40 | CipAttribute* CipInstance::Attribute( int aAttributeId ) const 41 | { 42 | CIPSTER_ASSERT( owning_class ); 43 | 44 | return owning_class->Attribute( CI_(), aAttributeId ); 45 | } 46 | 47 | 48 | const CipAttributes& CipInstance::Attributes() const 49 | { 50 | return CI_() == _I ? 51 | owning_class->AttributesI() : 52 | owning_class->AttributesC(); 53 | } 54 | -------------------------------------------------------------------------------- /source/src/cip/cipinstance.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (C) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef CIPINSTANCE_H_ 8 | #define CIPINSTANCE_H_ 9 | 10 | #include 11 | 12 | #include "cipattribute.h" 13 | #include "cipservice.h" 14 | 15 | 16 | /** 17 | * Class CipInstance 18 | * holds CIP intance info and instances may be contained within a #CipClass. 19 | */ 20 | class CipInstance 21 | { 22 | friend class CipClass; 23 | 24 | public: 25 | 26 | enum _CI 27 | { 28 | _I, // Feature pertains to the Instance with id > 0 29 | _C, // Feature pertains to the Class, i.e. instance 0. 30 | }; 31 | 32 | CipInstance( int aInstanceId ); 33 | 34 | virtual ~CipInstance(); 35 | 36 | int Id() const { return instance_id; } 37 | 38 | CipClass* Class() const { return owning_class; } 39 | 40 | /// If this a CipClass at instance_id == 0 then return _C, 41 | /// else return _I because it is an instance. 42 | _CI CI_() const 43 | { 44 | return reinterpret_cast(owning_class) == this ? 45 | _C : _I; 46 | } 47 | 48 | /** 49 | * Function Attribute 50 | * returns a CipAttribute of this instance or NULL if not found. 51 | */ 52 | CipAttribute* Attribute( int aAttributeId ) const; 53 | 54 | const CipAttributes& Attributes() const; 55 | 56 | void ShowAttributes() 57 | { 58 | const CipAttributes& all = Attributes(); 59 | 60 | for( CipAttributes::const_iterator it = all.begin(); 61 | it != all.end(); ++it ) 62 | { 63 | CIPSTER_TRACE_INFO( "id:%d\n", (*it)->Id() ); 64 | } 65 | } 66 | 67 | void* Data( const CipAttribute* aAttribute ) 68 | { 69 | return aAttribute->is_offset_from_instance_start ? 70 | (char*) this + aAttribute->where : 71 | (char*) aAttribute->where; 72 | } 73 | 74 | /** 75 | * Function Service 76 | * returns a CipService or NULL if not found. 77 | * If this instance is a CipClass (w/ instance_id == 0) then 78 | * the class service is returned, else the instance service is returned. 79 | */ 80 | CipService* Service( int aServiceId ) const; 81 | 82 | protected: 83 | 84 | int instance_id; ///< this instance's number (unique within the class) 85 | CipClass* owning_class; ///< class the instance belongs to or NULL if none. 86 | 87 | void setClass( CipClass* aClass ) { owning_class = aClass; } 88 | 89 | 90 | private: 91 | CipInstance( CipInstance& ); // private because not implemented 92 | CipInstance& operator=( const CipInstance& ); // private because not implemented 93 | }; 94 | 95 | typedef std::vector CipInstances; 96 | 97 | #endif // CIPINSTANCE_H_ 98 | -------------------------------------------------------------------------------- /source/src/cip/cipmessagerouter.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_CIPMESSAGEROUTER_H_ 7 | #define CIPSTER_CIPMESSAGEROUTER_H_ 8 | 9 | #include 10 | #include "ciptypes.h" 11 | #include "cipepath.h" 12 | #include "cipclass.h" 13 | #include "cipcommon.h" 14 | 15 | 16 | /** 17 | * Struct CipMessageRouterRequest 18 | * See Vol1 - 2-4.1 19 | */ 20 | class CipMessageRouterRequest : public Serializeable 21 | { 22 | public: 23 | 24 | CipMessageRouterRequest() : 25 | service( CIPServiceCode( 0 ) ) 26 | {} 27 | 28 | CipMessageRouterRequest( 29 | CIPServiceCode aService, 30 | const CipAppPath& aPath, 31 | const BufReader& aData ) : 32 | service( aService ), 33 | path( aPath ), 34 | data( aData ) 35 | {} 36 | 37 | CIPServiceCode Service() const { return service; } 38 | void SetService( CIPServiceCode aService ) { service = aService; } 39 | 40 | const CipAppPath& Path() const { return path; } 41 | void SetPathAttribute( int aId ) { path.SetAttribute( aId ); } 42 | 43 | const BufReader& Data() const { return data; } 44 | void SetData( const BufReader& aRdr ) { data = aRdr; } 45 | 46 | /** 47 | * Function DeserializeMRReq 48 | * parses the UCMM header consisting of: service, IOI size, IOI, 49 | * data into a request structure 50 | * 51 | * @param aCommand the serialized CPFD data item, i.e. CIP command 52 | * @return int - number of bytes consumed or -1 if error. 53 | */ 54 | int DeserializeMRReq( BufReader aCommand ); 55 | 56 | //----------------------------------------------------------- 57 | int Serialize( BufWriter aOutput, int aCtl = 0 ) const; 58 | int SerializedCount( int aCtl = 0) const; 59 | //---------------------------------------------------------- 60 | 61 | protected: 62 | 63 | CIPServiceCode service; 64 | CipAppPath path; 65 | BufReader data; 66 | }; 67 | 68 | 69 | class Cpf; 70 | 71 | /** 72 | * Class CipMessageRouterResponse 73 | * 74 | */ 75 | class CipMessageRouterResponse : public Serializeable 76 | { 77 | public: 78 | CipMessageRouterResponse( Cpf* aCpf, 79 | BufWriter aOutput = BufWriter( mmr_temp.data(), mmr_temp.size() ) ); 80 | 81 | void Clear(); 82 | 83 | Cpf* CPF() const { return cpf; } 84 | void SetCPF( Cpf* aCpf ) { cpf = aCpf; } 85 | 86 | /** 87 | * Function DeserializeMRRes 88 | * deserializes a message router response status as it comes back from a target. 89 | * It stops after the generic status information. After that a call to 90 | * this->Reader() will return a reader which gives access to the service 91 | * specific response. 92 | * 93 | * @return int - the number of bytes consumed. 94 | */ 95 | int DeserializeMRRes( BufReader aReply ); 96 | 97 | //----------------------------------------------------------- 98 | int Serialize( BufWriter aOutput, int aCtl = 0 ) const; 99 | int SerializedCount( int aCtl = 0 ) const; 100 | //---------------------------------------------------------- 101 | 102 | 103 | //----------------------------------------------------------- 104 | 105 | CipMessageRouterResponse& SetService( CIPServiceCode aService ) 106 | { 107 | reply_service = aService; 108 | return *this; 109 | } 110 | 111 | CIPServiceCode Service() const { return reply_service; } 112 | 113 | CipMessageRouterResponse& SetGenStatus( CipError aError ) 114 | { 115 | general_status = aError; 116 | return *this; 117 | } 118 | 119 | CipError GenStatus() const { return general_status; } 120 | 121 | /// Append an additional status word to response 122 | CipMessageRouterResponse& AddAdditionalSts( CipUint aStsWord ) 123 | { 124 | if( size_of_additional_status < DIM( additional_status ) ) 125 | { 126 | additional_status[ size_of_additional_status++ ] = aStsWord; 127 | } 128 | return *this; 129 | } 130 | 131 | int AdditionalStsCount() const { return size_of_additional_status; } 132 | 133 | ConnMgrStatus ExtStatus() const 134 | { 135 | return ConnMgrStatus( size_of_additional_status ? additional_status[ 0 ] : 0 ); 136 | } 137 | 138 | //----------------------------------------------------- 139 | 140 | /// Return a BufWriter which defines a buffer to be filled with the 141 | /// serialized reply for sending. 142 | BufWriter Writer() const { return data; } 143 | 144 | void WriterAdvance( int aCount ) 145 | { 146 | data = BufWriter( data ) + aCount; 147 | } 148 | 149 | void SetWriter( const BufWriter& w ) { data = w; } 150 | 151 | // Set the bound of a readable payload that exists beyond the received response 152 | // status on an originator's end. Prepares for a call to this->Reader(). 153 | void SetReader( const BufReader& r ) 154 | { 155 | data = r; 156 | written_size = r.size(); 157 | } 158 | 159 | void SetWrittenSize( int aSize ) { written_size = aSize; } 160 | int WrittenSize() const { return written_size; } 161 | 162 | void Show() const 163 | { 164 | CIPSTER_TRACE_INFO( "CipMessageRouterResponse:\n" ); 165 | CIPSTER_TRACE_INFO( " reply_service:0x%02x\n", reply_service ); 166 | CIPSTER_TRACE_INFO( " general_status:0x%02x\n", general_status ); 167 | //CIPSTER_TRACE_INFO( " size_of_additional_status:%d\n", size_of_additional_status ); 168 | 169 | for( int i=0; i mmr_temp; 206 | }; 207 | 208 | 209 | class CipMessageRouterClass : public CipClass 210 | { 211 | public: 212 | CipMessageRouterClass(); 213 | 214 | CipError OpenConnection( ConnectionData* aConn, Cpf* aCpf, 215 | ConnMgrStatus* extended_error_code ); // override 216 | 217 | /** 218 | * Function NotifyMR 219 | * notifies the MessageRouter that an explicit message (connected or unconnected) 220 | * has been received. This function is called from the encapsulation layer. 221 | * 222 | * @param aRequest is a parsed CipMessageRouterRequest 223 | * @param aResponse is where to put the reply, must fill in 224 | * CipMessageRouterResponse and its BufWriter (provided by Writer() ). 225 | * Then call SetWrittenSize, which is how caller knows the length. 226 | * Should not advance data.data(). 227 | * 228 | * @return EipStatus - kEipStatusError if error and caller is not to send any reply. 229 | * kEipStatusOkSend if caller is to send reply, which may contain 230 | * an error indication in general status field. 231 | */ 232 | static EipStatus NotifyMR( CipMessageRouterRequest* aRequest, 233 | CipMessageRouterResponse* aResponse ); 234 | 235 | /** 236 | * Function Init 237 | * initializes the message router support. 238 | * @return kEipStatusOk if class was initialized, otherwise kEipStatusError 239 | */ 240 | static EipStatus Init(); 241 | 242 | CipInstance* CreateInstance( int aInstanceId ); 243 | 244 | static EipStatus multiple_service_packet_service( CipInstance* instance, 245 | CipMessageRouterRequest* request, 246 | CipMessageRouterResponse* response ); 247 | }; 248 | 249 | #endif // CIPSTER_CIPMESSAGEROUTER_H_ 250 | -------------------------------------------------------------------------------- /source/src/cip/cipservice.h: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * Copyright (c) 2009, Rockwell Automation, Inc. 4 | * Copyright (C) 2016-2018, SoftPLC Corporation. 5 | * 6 | ******************************************************************************/ 7 | #ifndef CIPSERVICE_H_ 8 | #define CIPSERVICE_H_ 9 | 10 | #include 11 | #include 12 | 13 | 14 | /** 15 | * Enum CIPServiceCode 16 | * is the set of CIP service codes. 17 | * Common service codes range from 0x01 to 0x1c. Beyond that there can 18 | * be class or instance specific service codes and some may overlap. 19 | */ 20 | enum CIPServiceCode 21 | { 22 | kGetAttributeAll = 0x01, 23 | kSetAttributeAll = 0x02, 24 | kGetAttributeList = 0x03, 25 | kSetAttributeList = 0x04, 26 | kReset = 0x05, 27 | kStart = 0x06, 28 | kStop = 0x07, 29 | kCreate = 0x08, 30 | kDelete = 0x09, 31 | kMultipleServicePacket = 0x0a, 32 | kApplyAttributes = 0x0d, 33 | kGetAttributeSingle = 0x0e, 34 | kSetAttributeSingle = 0x10, 35 | kFindNextObjectInstance = 0x11, 36 | kRestore = 0x15, 37 | kSave = 0x16, 38 | kNoOperation = 0x17, 39 | kGetMember = 0x18, 40 | kSetMember = 0x19, 41 | kInsertMember = 0x1a, 42 | kRemoveMember = 0x1b, 43 | kGroupSync = 0x1c, 44 | 45 | // Start CIP class or instance specific services, probably should go in class specific area 46 | kForwardClose = 0x4e, 47 | kUnconnectedSend = 0x52, 48 | kForwardOpen = 0x54, 49 | kGetConnectionOwner = 0x5a, 50 | kLargeForwardOpen = 0x5b, 51 | // End CIP class or instance specific services 52 | }; 53 | 54 | 55 | /** 56 | * Typedef EipStatus (*CipServiceFunc)( CipInstance *, 57 | * CipMessageRouterRequest*, CipMessageRouterResponse*) 58 | * is a CIP service function pointer. 59 | * 60 | * @param aInstance which was referenced in the service request 61 | * @param aRequest holds "data" coming from client, and it includes a length. 62 | * @param aResponse where to put the response, do it into member "data" which is length 63 | * defined. Upon completions update data_length to how many bytes were filled in. 64 | * 65 | * @return EipStatus - EipOKSend if service could be executed successfully 66 | * and a response should be sent. 67 | */ 68 | typedef EipStatus (*CipServiceFunction)( CipInstance* aInstance, 69 | CipMessageRouterRequest* aRequest, CipMessageRouterResponse* aResponse ); 70 | 71 | 72 | /** 73 | * Class CipService 74 | * holds info for a CIP service to be contained within a CipClass. 75 | */ 76 | class CipService 77 | { 78 | public: 79 | CipService( const char* aServiceName, int aServiceId, 80 | CipServiceFunction aServiceFunction ) : 81 | service_name( aServiceName ), 82 | service_id( aServiceId ), 83 | service_function( aServiceFunction ) 84 | { 85 | // replies often or in 0x80 to service code, so stay below 86 | CIPSTER_ASSERT( aServiceId > 0 && aServiceId < 0x80 ); 87 | } 88 | 89 | virtual ~CipService() {} 90 | 91 | int Id() const { return service_id; } 92 | 93 | const std::string& ServiceName() const { return service_name; } 94 | 95 | CipServiceFunction service_function; 96 | 97 | protected: 98 | std::string service_name; 99 | int service_id; 100 | }; 101 | 102 | typedef std::vector CipServices; 103 | 104 | 105 | #endif // CIPSERVICE_H_ 106 | -------------------------------------------------------------------------------- /source/src/cip/ciptcpipinterface.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016-2018, SoftPLC Corporation. 3 | * 4 | ******************************************************************************/ 5 | #ifndef CIPTCPIPINTERFACE_H_ 6 | #define CIPTCPIPINTERFACE_H_ 7 | 8 | 9 | /** @file ciptcpipinterface.h 10 | * @brief Public interface of the TCP/IP Interface Object 11 | * 12 | */ 13 | 14 | #include 15 | #include "ciptypes.h" 16 | #include "cipclass.h" 17 | 18 | 19 | /** @brief Multicast Configuration struct, called Mcast config 20 | * 21 | */ 22 | struct MulticastAddressConfiguration 23 | { 24 | MulticastAddressConfiguration() : 25 | alloc_control( 0 ), 26 | reserved_zero( 0 ), 27 | number_of_allocated_multicast_addresses( 1 ), 28 | starting_multicast_address( 0 ) 29 | {} 30 | 31 | /// 0 for default multicast address generation algorithm; 32 | /// 1 for multicast addresses according to Num MCast and MCast Start Addr 33 | CipUsint alloc_control; 34 | 35 | CipUsint reserved_zero; ///< shall be zero 36 | 37 | /// Number of IP multicast addresses allocated 38 | CipUint number_of_allocated_multicast_addresses; 39 | 40 | /// Starting multicast address from which Num Mcast addresses are allocated 41 | CipUdint starting_multicast_address; 42 | }; 43 | 44 | 45 | /** 46 | * Struct CipTcpIpNetworkInterfaceConfiguration 47 | * is for holding TCP/IP interface information 48 | */ 49 | struct CipTcpIpInterfaceConfiguration 50 | { 51 | CipTcpIpInterfaceConfiguration() : 52 | ip_address( 0 ), 53 | network_mask( 0 ), 54 | gateway( 0 ), 55 | name_server( 0 ), 56 | name_server_2( 0 ) 57 | {} 58 | 59 | // network byte order for each of these: 60 | CipUdint ip_address; 61 | CipUdint network_mask; 62 | CipUdint gateway; 63 | CipUdint name_server; 64 | CipUdint name_server_2; 65 | 66 | std::string domain_name; 67 | }; 68 | 69 | 70 | class CipTCPIPInterfaceInstance : public CipInstance 71 | { 72 | friend class CipTCPIPInterfaceClass; 73 | 74 | public: 75 | CipTCPIPInterfaceInstance( int aInstanceId ); 76 | 77 | static CipUint inactivity_timeout_secs; 78 | 79 | protected: 80 | // Attributes of a TCP/IP Interface instance are numbered # 81 | 82 | /// #1 TCP status with 1 we indicate that we got a valid 83 | /// configuration from DHCP or BOOTP or non volatile storage 84 | CipDword status; 85 | 86 | /// #2 This is a default value meaning that it is a DHCP client 87 | /// see Vol2 5-4.3.2.2: 88 | CipDword configuration_capability; 89 | 90 | /// #3 This is a TCP/IP object attribute. For now it is always zero 91 | /// and is not used for anything. 92 | CipDword configuration_control; 93 | 94 | /// #5 IP, network mask, gateway, name server 1 & 2, domain name 95 | CipTcpIpInterfaceConfiguration interface_configuration; 96 | 97 | /// #6 Hostname, static so its shared betweeen instances of this class. 98 | static std::string hostname; 99 | 100 | /** 101 | * #8 the time to live value to be used for multi-cast connections 102 | * 103 | * Currently we implement it non set-able and with the default value of 1. 104 | */ 105 | uint8_t time_to_live; 106 | 107 | /** 108 | * #9 The multicast configuration for this device 109 | * 110 | * Currently we implement it non set-able and use the default 111 | * allocation algorithm 112 | */ 113 | MulticastAddressConfiguration multicast_configuration; 114 | 115 | EipStatus configureNetworkInterface( 116 | const char* ip_address, 117 | const char* subnet_mask, 118 | const char* gateway ); 119 | 120 | //---------------------------------------------------------- 121 | 122 | static EipStatus get_attr_4( CipInstance* aInstance, 123 | CipAttribute* aAttribute, 124 | CipMessageRouterRequest* aRequest, 125 | CipMessageRouterResponse* aResponse ); 126 | 127 | static EipStatus get_attr_5( CipInstance* aInstance, 128 | CipAttribute* aAttribute, 129 | CipMessageRouterRequest* aRequest, 130 | CipMessageRouterResponse* aResponse ); 131 | 132 | static EipStatus get_multicast_config( CipInstance* aInstance, 133 | CipAttribute* aAttribute, 134 | CipMessageRouterRequest* aRequest, 135 | CipMessageRouterResponse* aResponse ); 136 | 137 | static EipStatus set_multicast_config( CipInstance* aInstance, 138 | CipAttribute* aAttribute, 139 | CipMessageRouterRequest* aRequest, 140 | CipMessageRouterResponse* aResponse ); 141 | 142 | static EipStatus get_attr_7( CipInstance* aInstance, 143 | CipAttribute* aAttribute, 144 | CipMessageRouterRequest* aRequest, 145 | CipMessageRouterResponse* aResponse ); 146 | 147 | static EipStatus set_attr_13( CipInstance* aInstance, 148 | CipAttribute* aAttribute, 149 | CipMessageRouterRequest* aRequest, 150 | CipMessageRouterResponse* aResponse ); 151 | 152 | static EipStatus set_TTL( CipInstance* aInstance, 153 | CipAttribute* aAttribute, 154 | CipMessageRouterRequest* aRequest, 155 | CipMessageRouterResponse* aResponse ); 156 | 157 | //--------------------------------------------------------- 158 | 159 | //----------------------------------------------------- 160 | 161 | // overload GetAttributeAll because we have to fill in gaps 162 | // for "not implemented" attributes. 163 | // This is supplied because the TCIP/IP class spec wants ALL attributes to 164 | // be returned up to and including the last implemented one, WITH NO GAPS. 165 | // This means we have to make up unimplemented attributes to fill in the holes. 166 | // The standard CipClass::GetAttributeAll() function does not do this. 167 | static EipStatus get_all( CipInstance* aInstance, 168 | CipMessageRouterRequest* aRequest, CipMessageRouterResponse* aResponse ); 169 | 170 | //---------------------------------------------------- 171 | }; 172 | 173 | 174 | class CipTCPIPInterfaceClass : public CipClass 175 | { 176 | public: 177 | CipTCPIPInterfaceClass(); 178 | 179 | /// overload Instance() to return proper derived CipInstance type 180 | CipTCPIPInterfaceInstance* Instance( int aInstanceId ); 181 | 182 | /** 183 | * Function Init 184 | * initializes the data structures of the TCP/IP interface objects. 185 | */ 186 | static EipStatus Init(); 187 | 188 | /** 189 | * Function ShutdownTcpIpInterface 190 | * cleans up the allocated data of the TCP/IP interface objects 191 | */ 192 | static void Shutdown(); 193 | 194 | static const MulticastAddressConfiguration& MultiCast( int aInstanceId ); 195 | 196 | static const CipTcpIpInterfaceConfiguration& InterfaceConf( int aInstanceId ); 197 | 198 | static uint8_t TTL( int aInstanceId ); 199 | 200 | /// Return an instance's IP address in network byte oder. The instance 201 | /// ids for this class must be allocated contiguously starting at 1. 202 | static CipUdint IpAddress( int aInstanceId ); 203 | 204 | /** @ingroup CIP_API 205 | * @brief Configure the data of the network interface of the device 206 | * 207 | * This function setup the data of the network interface needed by CIPster. 208 | * The multicast address is automatically calculated from he given data. 209 | * 210 | * @param aInstanceId is the CipTCPIPInterfaceInstance instance id, starting at 1. 211 | * These objects reside in class CipTCPIPInterfaceClass, and there normally only 1. 212 | * @param ip_address the current IP address of the device 213 | * @param subnet_mask the subnet mask to be used 214 | * @param gateway_address the gateway address 215 | * @return kEipStatusOk if the configuring worked otherwise EIP_ERROR 216 | */ 217 | static EipStatus ConfigureNetworkInterface( int aInstanceId, const char* ip_address, 218 | const char* subnet_mask, const char* gateway_address ); 219 | 220 | /** @ingroup CIP_API 221 | * @brief Configure the domain name of the device 222 | * @param domain_name the domain name to be used 223 | */ 224 | static void ConfigureDomainName( int aInstanceId, const char* aDomainName ); 225 | 226 | /** @ingroup CIP_API 227 | * @brief Configure the host name of the device 228 | * @param aHostName the host name to be used 229 | */ 230 | static void ConfigureHostName( int aInstanceId, const char* aHostName ); 231 | }; 232 | 233 | #endif // CIPTCPIPINTERFACE_H_ 234 | -------------------------------------------------------------------------------- /source/src/cip/ciptypes.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (C) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | #ifndef CIPSTER_CIPTYPES_H_ 7 | #define CIPSTER_CIPTYPES_H_ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "typedefs.h" 15 | #include "trace.h" 16 | #include "cipster_user_conf.h" 17 | #include "ciperror.h" 18 | #include "byte_bufs.h" 19 | 20 | 21 | /** 22 | * Enum ClassIds 23 | * is a set of common classs ids. 24 | */ 25 | enum ClassIds 26 | { 27 | kCipIdentityClass = 0x01, 28 | kCipMessageRouterClass = 0x02, 29 | kCipAssemblyClass = 0x04, 30 | kCipConnectionClass = 0x05, 31 | kCipConnectionManagerClass = 0x06, 32 | kCipRegisterClass = 0x07, 33 | kCipTcpIpInterfaceClass = 0xF5, 34 | kCipEthernetLinkClass = 0xF6, 35 | }; 36 | 37 | 38 | /** 39 | * Enum CipDataType 40 | * is the set of encoded CIP data types for CIP Messages 41 | */ 42 | enum CipDataType 43 | { 44 | kCipAny = 0x00, ///< data type that can not be directly encoded 45 | kCipBool = 0xC1, ///< boolean data type 46 | kCipSint = 0xC2, ///< 8-bit signed integer 47 | kCipInt = 0xC3, ///< 16-bit signed integer 48 | kCipDint = 0xC4, ///< 32-bit signed integer 49 | kCipLint = 0xC5, ///< 64-bit signed integer 50 | kCipUsint = 0xC6, ///< 8-bit unsignedeger 51 | kCipUint = 0xC7, ///< 16-bit unsigned 52 | kCipUdint = 0xC8, ///< 32-bit unsigned 53 | kCipUlint = 0xC9, ///< 64-bit unsigned 54 | kCipReal = 0xCA, ///< Single precision floating point 55 | kCipLreal = 0xCB, ///< Double precision floating point 56 | kCipStime = 0xCC, ///< Synchronous time information*, type of DINT 57 | kCipDate = 0xCD, ///< Date only 58 | kCipTimeOfDay = 0xCE, ///< Time of day 59 | kCipDateAndTime = 0xCF, ///< Date and time of day 60 | kCipString = 0xD0, ///< Character string, 1 byte per character 61 | kCipByte = 0xD1, ///< 8-bit bit string 62 | kCipWord = 0xD2, ///< 16-bit bit string 63 | kCipDword = 0xD3, ///< 32-bit bit string 64 | kCipLword = 0xD4, ///< 64-bit bit string 65 | kCipString2 = 0xD5, ///< Character string, 2 byte per character 66 | kCipFtime = 0xD6, ///< Duration in micro-seconds, high resolution; range of DINT 67 | kCipLtime = 0xD7, ///< Duration in micro-seconds, high resolution, range of LINT 68 | kCipItime = 0xD8, ///< Duration in milli-seconds, short; range of INT 69 | kCipStringN = 0xD9, ///< Character string, N byte per character 70 | kCipShortString = 0xDA, /**< Character string, 1 byte per character, 1 byte 71 | * length indicator */ 72 | kCipTime = 0xDB, ///< Duration in milli-seconds; range of DINT 73 | // kCipEpath = 0xDC, ///< CIP path segments 74 | kCipEngUnit = 0xDD, ///< Engineering Units 75 | kCipStringI = 0xDE, ///< International Character String 76 | 77 | // definition of some CIP structs 78 | // need to be validated in IEC 61131-3 subclause 2.3.3 79 | // TODO: Check these codes 80 | kCipUsintUsint = 0xA0, ///< Used for CIP Identity attribute 4 Revision 81 | kCip6Usint = 0xA2, ///< Struct for MAC Address (six USINTs) 82 | kCipMemberList = 0xA3, ///< 83 | kCipByteArray = 0xA4, ///< 84 | 85 | // non standard, could assign any value here 86 | kCipByteArrayLength = 0xA5, 87 | }; 88 | 89 | /** 90 | * Enum UdpCommunicationDirection 91 | * is a set of two directions: inbound or outbound data in the parlance of 92 | * CIP which terms them as consuming or producing. 93 | */ 94 | enum UdpDirection 95 | { 96 | kUdpConsuming = 0, ///< Consuming direction; receiver 97 | kUdpProducing = 1, ///< Producing direction; sender 98 | }; 99 | 100 | 101 | struct CipRevision 102 | { 103 | CipRevision( uint8_t aMajor = 0, uint8_t aMinor = 0 ) : 104 | major_revision( aMajor ), 105 | minor_revision( aMinor ) 106 | {} 107 | 108 | uint8_t major_revision; 109 | uint8_t minor_revision; 110 | }; 111 | 112 | 113 | class CipInstance; 114 | class CipAttribute; 115 | class CipClass; 116 | class CipMessageRouterRequest; 117 | class CipMessageRouterResponse; 118 | class CipConn; 119 | class Cpf; 120 | 121 | 122 | // Macros to create integer with bitfields 123 | #define MASK1( a ) ( 1 << (a) ) 124 | #define MASK2( a, b ) ( 1 << (a) | 1 << (b) ) 125 | #define MASK3( a, b, c ) ( 1 << (a) | 1 << (b) | 1 << (c) ) 126 | #define MASK4( a, b, c, d ) ( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) ) 127 | #define MASK5( a, b, c, d, e ) \ 128 | ( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) ) 129 | 130 | #define MASK6( a, b, c, d, e, f ) \ 131 | ( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) | 1 << (f) ) 132 | 133 | #define MASK7( a, b, c, d, e, f, g ) \ 134 | ( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) | 1 << (f) | 1 << (g) ) 135 | 136 | #define MASK8( a, b, c, d, e, f, g, h ) \ 137 | ( 1 << (a) | 1 << (b) | 1 << (c) | 1 << (d) | 1 << (e) | 1 << (f) | \ 138 | 1 << (g) | 1 << (h) ) 139 | 140 | #endif // CIPSTER_CIPTYPES_H_ 141 | -------------------------------------------------------------------------------- /source/src/enet_encap/byte_bufs.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2016-2018, SoftPLC Corporation. 3 | * 4 | ******************************************************************************/ 5 | 6 | #include 7 | #include 8 | 9 | 10 | #if defined(DEBUG) && defined(__linux__) 11 | #include 12 | 13 | #define TRACEZ 32 14 | 15 | static void stack_dump( const char* aContext ) 16 | { 17 | void* trace[TRACEZ]; 18 | char** strings; 19 | 20 | // see http://www.linuxjournal.com/article/6391 for info on backtrace() 21 | // in a signal handler. 22 | int trace_size = backtrace( trace, TRACEZ ); 23 | 24 | strings = backtrace_symbols( trace, trace_size ); 25 | 26 | printf( "%s:\n", aContext ); 27 | for( int i = 0; i < trace_size; ++i ) 28 | printf( " bt[%d] %s\n", i, strings[i] ); 29 | 30 | free( strings ); 31 | } 32 | #else 33 | inline void stack_dump( const char* aContext ) {} // nothing 34 | #endif 35 | 36 | 37 | #ifdef HAVE_ICONV 38 | #include 39 | 40 | #define UNICODE "UTF-16LE" // UTF-16LE is UNICODE with fixes to shortsighted-ness. 41 | 42 | #define UTF8 "UTF-8" // A way to deal with all unicode chars in a 43 | // platform independent way. 44 | 45 | /** 46 | * Class IConv 47 | * is a wrapper to customize the general iconv library to convert to and 48 | * from UTF8 to and from little endian UNICODE. 49 | * 50 | * @author Dick Hollenbeck, SoftPLC Corporation 51 | */ 52 | class IConv 53 | { 54 | public: 55 | IConv() 56 | { 57 | to_unicode = iconv_open( UNICODE, UTF8 ); 58 | 59 | if( to_unicode == iconv_t(-1) ) 60 | throw std::runtime_error( "error from iconv_open(\"" UNICODE "\", \"" UTF8 "\")" ); 61 | 62 | to_utf8 = iconv_open( UTF8, UNICODE ); 63 | 64 | if( to_utf8 == iconv_t(-1) ) 65 | throw std::runtime_error( "error from iconv_open(\"" UTF8 "\", \"" UNICODE "\" )" ); 66 | } 67 | 68 | ~IConv() 69 | { 70 | iconv_close( to_unicode ); 71 | iconv_close( to_utf8 ); 72 | } 73 | 74 | // return the number of bytes consumed at destination 75 | int ToUTF8( char** src_ptr, size_t* src_size, char** dst_ptr, size_t* dst_size ) 76 | { 77 | size_t dst_startz = *dst_size; 78 | size_t r = ::iconv( to_utf8, src_ptr, src_size, dst_ptr, dst_size ); 79 | 80 | if( r == size_t(-1) ) 81 | { 82 | if( errno == E2BIG ) 83 | r = dst_startz; 84 | else 85 | throw std::runtime_error( "invalid UNICODE" ); 86 | } 87 | 88 | return r; 89 | } 90 | 91 | // return the number of bytes consumed at destination 92 | int ToUNICODE( char** src_ptr, size_t* src_size, char** dst_ptr, size_t* dst_size ) 93 | { 94 | size_t dst_startz = *dst_size; 95 | size_t r = ::iconv( to_unicode, src_ptr, src_size, dst_ptr, dst_size ); 96 | 97 | if( r == size_t(-1) ) 98 | { 99 | if( errno == E2BIG ) 100 | r = dst_startz; 101 | else 102 | throw std::runtime_error( "invalid UTF8" ); 103 | } 104 | 105 | return r; 106 | } 107 | 108 | private: 109 | iconv_t to_unicode; 110 | iconv_t to_utf8; 111 | }; 112 | 113 | static IConv convert; 114 | 115 | #if defined(TEST_BYTE_BUFS) 116 | const int WORKZ = 32; // force multiple iconv() loops when testing. 117 | #else 118 | const int WORKZ = 256; 119 | #endif 120 | 121 | #endif 122 | 123 | 124 | void BufWriter::overrun() const 125 | { 126 | stack_dump( "write > limit" ); 127 | throw std::overflow_error( "write > limit" ); 128 | } 129 | 130 | 131 | BufWriter& BufWriter::put_SHORT_STRING( const std::string& aString, bool doEvenByteCountPadding ) 132 | { 133 | put8( (uint8_t) aString.size() ); 134 | append( (uint8_t*) aString.c_str(), aString.size() ); 135 | 136 | // !(size() & 1) means length is even, but since length of length byte 137 | // itself is odd sum can be odd when length is even. 138 | if( doEvenByteCountPadding && !(aString.size() & 1) ) 139 | put8( 0 ); 140 | 141 | return *this; 142 | } 143 | 144 | 145 | BufWriter& BufWriter::put_STRING( const std::string& aString, bool doEvenByteCountPadding ) 146 | { 147 | put16( aString.size() ); 148 | 149 | append( (uint8_t*) aString.c_str(), aString.size() ); 150 | if( doEvenByteCountPadding && ( aString.size() & 1 ) ) 151 | put8( 0 ); 152 | return *this; 153 | } 154 | 155 | 156 | BufWriter& BufWriter::put_STRING2( const std::string& aString ) 157 | { 158 | put16( aString.size() ); 159 | 160 | #if HAVE_ICONV 161 | 162 | char buf[WORKZ]; 163 | char* src_ptr = (char*) aString.c_str(); 164 | size_t src_size = aString.size(); 165 | 166 | while( src_size ) 167 | { 168 | char* dst_ptr = buf; 169 | size_t dst_size = sizeof(buf); 170 | 171 | try 172 | { 173 | convert.ToUNICODE( &src_ptr, &src_size, &dst_ptr, &dst_size ); 174 | } 175 | catch( const std::runtime_error& ex ) 176 | { 177 | CIPSTER_TRACE_ERR( "%s: ERROR '%s'\n", __func__, ex.what() ); 178 | break; 179 | } 180 | 181 | append( (uint8_t*) buf, sizeof(buf) - dst_size ); 182 | } 183 | 184 | #else 185 | 186 | for( unsigned i = 0; i < aString.size(); ++i ) 187 | put16( (unsigned char) aString[i] ); 188 | 189 | #endif 190 | 191 | return *this; 192 | } 193 | 194 | 195 | 196 | //----------------------------------------------------------------- 197 | 198 | void BufReader::overrun() const 199 | { 200 | stack_dump( "read > limit" ); 201 | throw std::range_error( "read > limit" ); 202 | } 203 | 204 | 205 | std::string BufReader::get_SHORT_STRING( bool ExpectPossiblePaddingToEvenByteCount ) 206 | { 207 | std::string ret; 208 | unsigned len = get8(); 209 | 210 | // !(len & 1) means length is even, but since length of length byte 211 | // itself is odd, total sum is odd when length is even. 212 | bool eat_pad = ExpectPossiblePaddingToEvenByteCount && !(len & 1); 213 | 214 | if( len + eat_pad > size() ) 215 | overrun(); 216 | 217 | ret.append( (char*) start, len ); 218 | 219 | start += len + eat_pad; 220 | 221 | return ret; 222 | } 223 | 224 | 225 | std::string BufReader::get_STRING( bool ExpectPossiblePaddingToEvenByteCount ) 226 | { 227 | std::string ret; 228 | unsigned len = get16(); 229 | 230 | bool eat_pad = ExpectPossiblePaddingToEvenByteCount && (len & 1); 231 | 232 | if( len + eat_pad > size() ) 233 | overrun(); 234 | 235 | ret.append( (char*) start, len ); 236 | 237 | start += len + eat_pad; 238 | 239 | return ret; 240 | } 241 | 242 | 243 | std::string BufReader::get_STRING2() 244 | { 245 | std::string ret; 246 | unsigned len = get16(); 247 | 248 | #ifdef HAVE_ICONV 249 | 250 | char buf[WORKZ]; 251 | char* src_ptr = (char*) data(); 252 | size_t src_size = len * 2; 253 | 254 | // Advance this BufReader by full "len*2" now using a function which 255 | // also does overrun protection. Prior to this, we snapshotted the 256 | // src_ptr to just past the len field. 257 | operator+=( src_size ); 258 | 259 | while( src_size ) 260 | { 261 | char* dst_ptr = buf; 262 | size_t dst_size = sizeof(buf); 263 | 264 | try 265 | { 266 | convert.ToUTF8( &src_ptr, &src_size, &dst_ptr, &dst_size ); 267 | } 268 | catch( const std::runtime_error& ex ) 269 | { 270 | CIPSTER_TRACE_ERR( "%s: ERROR '%s'\n", __func__, ex.what() ); 271 | 272 | // std::string ret will be abbreviated, but we consumed full STRING2. 273 | break; 274 | } 275 | 276 | ret.append( buf, sizeof(buf) - dst_size ); 277 | } 278 | 279 | #else 280 | 281 | for( unsigned i = 0; i < len; ++i ) 282 | ret += (char) get16(); 283 | #endif 284 | 285 | return ret; 286 | } 287 | 288 | 289 | //-------------------------------------------------------------- 290 | 291 | int ByteSerializer::Serialize( BufWriter aOutput, int aCtl ) const 292 | { 293 | aOutput.append( BufReader(*this) ); 294 | return size(); 295 | } 296 | 297 | 298 | int ByteSerializer::SerializedCount( int aCtl ) const 299 | { 300 | return size(); 301 | } 302 | 303 | 304 | #if !BYTEBUFS_INLINE 305 | #include 306 | #endif 307 | 308 | 309 | #if defined(TEST_BYTE_BUFS) 310 | 311 | // The CMake build target for this is "test_byte_bufs" 312 | // Do "make help" in the _library's_ build directory to see that. 313 | // compile only with C++11 or greater? 314 | // https://en.cppreference.com/w/cpp/language/string_literal 315 | 316 | uint8_t buf[400]; 317 | 318 | 319 | int main( int argc, char** argv ) 320 | { 321 | const char16_t unicode[] = u"This is some sample boring UNICODE text for input."; 322 | const char utf8[] = u8"ASCII is also UTF8, but reverse is not true, some trivia there."; 323 | 324 | BufWriter w( buf, sizeof(buf) ); 325 | 326 | // make a STRING2 in buf[]. The -2 removes trailing null char16_t. 327 | w.put16( (sizeof(unicode)-2)/2 ); 328 | for( unsigned i=0; i<(sizeof(unicode)-2)/2; ++i ) 329 | w.put16( unicode[i] ); 330 | 331 | // Read back the STRING2 using this BufReader 'r'. 332 | BufReader r( buf, w.data() - buf ); 333 | std::string s = r.get_STRING2(); 334 | printf( "from UNICODE:'%s'\n", s.c_str() ); 335 | 336 | // Save utf8 as UNICODE and read it back: 337 | s = utf8; 338 | 339 | BufWriter w2( buf, sizeof buf ); 340 | 341 | w2.put_STRING2( s ); 342 | 343 | BufReader r2( buf, w2.data() - buf ); 344 | 345 | s = r2.get_STRING2(); 346 | 347 | printf( "round trip UTF8->UNICODE->UTF8:'%s'\n", s.c_str() ); 348 | } 349 | #endif 350 | -------------------------------------------------------------------------------- /source/src/enet_encap/networkhandler.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * Copyright (c) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef NETWORKHANDLER_H_ 8 | #define NETWORKHANDLER_H_ 9 | 10 | #include 11 | 12 | #include "sockaddr.h" 13 | #include "../cip/ciptypes.h" 14 | 15 | 16 | /** 17 | * Function NetworkHandlerInitialize 18 | * starts a TCP/UDP listening socket to accept connections. 19 | */ 20 | EipStatus NetworkHandlerInitialize(); 21 | 22 | EipStatus NetworkHandlerProcessOnce(); 23 | 24 | EipStatus NetworkHandlerFinish(); 25 | 26 | /** 27 | * Function CloseSocket 28 | * closes @a aSocket 29 | */ 30 | void CloseSocket( int aSocket ); 31 | 32 | bool SocketAsync( int aSocket, bool isAsync = true ); 33 | 34 | 35 | /** 36 | * Function SendUdpData 37 | * sends the bytes provided in @a aOutput to the UDP node given by @a aSockAddr 38 | * using @a aSocket. 39 | * 40 | * @param aSockAddr is the "send to" address and could be multicast or unicast or broadcast. 41 | * @param aSocket is the socket descriptor to send on 42 | * @param aOutput is the data to send and its length 43 | * @throw socket_error - if @a aOutput contains more than what was sent. 44 | */ 45 | void SendUdpData( const SockAddr& aSockAddr, int aSocket, BufReader aOutput ); 46 | 47 | 48 | /** 49 | * Class UdpSocket 50 | * holds a UDP socket handle and also optional group membership with 51 | * reference counting for such membership. 52 | */ 53 | class UdpSocket 54 | { 55 | friend class UdpSocketMgr; 56 | 57 | public: 58 | UdpSocket( const SockAddr& aSockAddr, int aSocket ) : 59 | m_sockaddr( aSockAddr ), 60 | m_socket( aSocket ), 61 | m_ref_count( 1 ), 62 | m_underlying( 0 ) 63 | { 64 | } 65 | 66 | ~UdpSocket() 67 | { 68 | if( m_socket != kSocketInvalid ) 69 | { 70 | CloseSocket( m_socket ); 71 | m_socket = kSocketInvalid; 72 | } 73 | } 74 | 75 | /** 76 | * Function Send 77 | * send a packet on UDP. 78 | * @throw socket_error if a problem sending 79 | */ 80 | void Send( const SockAddr& aAddr, const BufReader& aReader ) 81 | { 82 | ::SendUdpData( aAddr, m_socket, aReader ); 83 | } 84 | 85 | int Recv( SockAddr* aAddr, const BufWriter& aWriter ) 86 | { 87 | socklen_t from_addr_length = SADDRZ; 88 | 89 | return recvfrom( m_socket, (char*) aWriter.data(), aWriter.capacity(), 0, 90 | *aAddr, &from_addr_length ); 91 | } 92 | 93 | const SockAddr& SocketAddress() const { return m_sockaddr; } 94 | int h() const { return m_socket; } 95 | int RefCount() const { return m_ref_count; } 96 | 97 | void Show() const 98 | { 99 | CIPSTER_TRACE_INFO( "UdpSocket[%d] bound:%s mcast:%s\n", 100 | m_socket, 101 | m_sockaddr.Format().c_str(), 102 | m_underlying ? m_underlying->m_sockaddr.Format().c_str() : "no" ); 103 | } 104 | 105 | private: 106 | SockAddr m_sockaddr; // what the m_socket is bound to with bind() 107 | int m_socket; 108 | int m_ref_count; 109 | UdpSocket* m_underlying; // used by Multicast only. 110 | }; 111 | 112 | 113 | /** 114 | * Class UdpSocketMgr 115 | * manages UDP sockets in the form of UdpSocket instances. 116 | * Since a UDP socket can be used for multiple inbound 117 | * and outbound UDP frames, without regard to who the recipient or sender is, 118 | * we re-use UDP sockets accross multiple I/O connections. This way we do 119 | * not create more than the minimum number of sockets, and we should not have 120 | * to use SO_REUSEADDR. 121 | */ 122 | class UdpSocketMgr 123 | { 124 | public: 125 | 126 | typedef std::vector sockets; 127 | typedef sockets::iterator sock_iter; 128 | typedef sockets::const_iterator sock_citer; 129 | 130 | #define DEFAULT_BIND_IPADDR INADDR_ANY 131 | //#define DEFAULT_BIND_IPADDR ntohl( CipTCPIPInterfaceClass::IpAddress( 1 ) ) 132 | 133 | /** 134 | * Function GrabSocket 135 | * registers use of a UDP socket bound to aSockAddr, and creates it if 136 | * necessary. Additionally, if aMulticastAddr is not NULL, it will register 137 | * use of that group and join it to the socket associated with aSockAddr if it 138 | * is not already. Since reference counting is used both for the base socket 139 | * and for the groups, you must balance the number of calls to GrabSocket with 140 | * those to ReleaseSocket. 141 | */ 142 | static UdpSocket* GrabSocket( const SockAddr& aSockAddr, const SockAddr* aMulticastAddr=NULL ); 143 | 144 | /** 145 | * Function RelaseSocket 146 | * reduces the reference count associated with aUdpSocket which was obtained 147 | * earlier with GrabSocket. aUdpSocket may be a multicast group address if 148 | * you want to drop a group membership, but that will only happen when the 149 | * group's reference count goes to zero. 150 | */ 151 | static bool ReleaseSocket( UdpSocket* aUdpSocket ); 152 | static sockets& GetAllSockets() { return m_sockets; } 153 | 154 | protected: 155 | 156 | /** 157 | * Function createSocket 158 | * creates a UDP socket and binds it to aSockAddr. 159 | * 160 | * @param aSockAddr tells how to setup the socket. 161 | * @return int - socket on success or kSocketInvalid on error 162 | */ 163 | static int createSocket( const SockAddr& aSockAddr ); 164 | 165 | // Allocate and initialize a new UdpSocket 166 | static UdpSocket* alloc( const SockAddr& aSockAddr, int aSocket ); 167 | 168 | static void free( UdpSocket* aUdpSocket ); 169 | 170 | // find aSockAddr in aList, return it or NULL if not found. 171 | static UdpSocket* find( const SockAddr& aSockAddr, const sockets& aList ); 172 | 173 | static sockets m_sockets; 174 | static sockets m_multicast; // these piggyback on a m_socket entry 175 | static sockets m_free; // recycling bin 176 | }; 177 | 178 | 179 | #endif // NETWORKHANDLER_H_ 180 | -------------------------------------------------------------------------------- /source/src/enet_encap/sockaddr.cc: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * Copyright (C) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "sockaddr.h" 8 | 9 | #if defined(__linux__) || defined(__APPLE__) 10 | #include 11 | #endif 12 | 13 | #if defined(__APPLE__) 14 | #include 15 | #endif 16 | 17 | std::string IpAddrStr( in_addr aIP ) 18 | { 19 | // inet_ntoa uses a static buffer, so capture that into a std::string 20 | // for safe keeping. 21 | return inet_ntoa( aIP ); 22 | } 23 | 24 | 25 | std::string strerrno() 26 | { 27 | char buf[256]; 28 | 29 | buf[0] = 0; 30 | 31 | #if defined(__linux__) 32 | // There are two versions of sterror_r() depending on age of glibc, try 33 | // and handle both of them with this: 34 | uintptr_t result = (uintptr_t) strerror_r( errno, buf, sizeof buf ); 35 | 36 | return buf[0] ? buf : (char*) result; 37 | 38 | #elif defined(_WIN32) 39 | 40 | // I don't think this works well, don't know why, its Windows....false advertising? 41 | int len = FormatMessage( 42 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 43 | NULL, // lpsource 44 | WSAGetLastError(), // message id 45 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 46 | buf, 47 | sizeof buf, 48 | NULL ); 49 | 50 | if( !buf[0] ) // insurance policy against above not working. 51 | len = snprintf( buf, sizeof buf, "%d", WSAGetLastError() ); 52 | 53 | return std::string( buf, len ); 54 | #endif 55 | } 56 | 57 | 58 | socket_error::socket_error() : 59 | std::runtime_error( strerrno() ), 60 | #if defined(__linux__) 61 | error_code( errno ) 62 | #elif defined(_WIN32) 63 | error_code( WSAGetLastError() ) 64 | #endif 65 | {} 66 | 67 | 68 | SockAddr::SockAddr( unsigned aPort, unsigned aIP ) 69 | { 70 | sa.sin_family = AF_INET; 71 | sa.sin_port = htons( aPort ); 72 | sa.sin_addr.s_addr = htonl( aIP ); 73 | 74 | memset( &sa.sin_zero, 0, sizeof sa.sin_zero ); 75 | } 76 | 77 | 78 | SockAddr::SockAddr( const char* aNameOrIPAddr, unsigned aPort ) 79 | { 80 | sa.sin_family = AF_INET; 81 | sa.sin_port = htons( aPort ); 82 | 83 | sa.sin_addr.s_addr = inet_addr( aNameOrIPAddr ); 84 | if( sa.sin_addr.s_addr == INADDR_NONE ) 85 | { 86 | hostent* ent = gethostbyname( aNameOrIPAddr ); 87 | if( !ent ) 88 | { 89 | const char* errmsg = "gethostbyname"; 90 | 91 | switch( h_errno ) 92 | { 93 | case HOST_NOT_FOUND: errmsg = "host is unknown"; break; 94 | case NO_DATA: errmsg = "host has no IP"; break; 95 | case NO_RECOVERY: errmsg = "name server error"; break; 96 | case TRY_AGAIN: errmsg = "try again later"; break; 97 | } 98 | 99 | throw socket_error( errmsg, h_errno ); 100 | } 101 | 102 | sa.sin_addr.s_addr = *(int*) ent->h_addr_list[0]; 103 | } 104 | 105 | memset( &sa.sin_zero, 0, sizeof sa.sin_zero ); 106 | } 107 | 108 | 109 | std::string SockAddr::Format() const 110 | { 111 | char buf[32]; 112 | int len = snprintf( buf, sizeof buf, ":%d", Port() ); 113 | 114 | std::string ret = AddrStr(); 115 | 116 | ret.append( buf, len ); 117 | 118 | return ret; 119 | } 120 | -------------------------------------------------------------------------------- /source/src/enet_encap/sockaddr.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2016-2018, SoftPLC Corporation. 3 | * 4 | ******************************************************************************/ 5 | 6 | #ifndef CIPSTER_SOCKADDR_H_ 7 | #define CIPSTER_SOCKADDR_H_ 8 | 9 | #include 10 | #include 11 | 12 | #if defined(__linux__) 13 | #include 14 | #include 15 | #elif defined(_WIN32) 16 | #undef _WINSOCKAPI_ // suppress Mingw32's "Please include winsock2.h before windows.h" 17 | #include 18 | #include 19 | #elif defined(__APPLE__) 20 | #include 21 | #include 22 | #endif 23 | 24 | #include 25 | 26 | 27 | const int SADDRZ = sizeof(sockaddr); 28 | 29 | std::string IpAddrStr( in_addr aIP ); 30 | 31 | /** 32 | * Function strerrno 33 | * returns a string containing text generated by the OS for the current value 34 | * of errno or WSAGetLastError() 35 | */ 36 | std::string strerrno(); 37 | 38 | 39 | /** 40 | * Class socket_error 41 | * holds both an integer error number and a textual error message, and can 42 | * be conveniently thrown. 43 | */ 44 | class socket_error : public std::runtime_error 45 | { 46 | public: 47 | 48 | /** 49 | * Constructor socket_error() 50 | * grabs current errno or WSAGetLastError() and its OS associated 51 | * text message using std::string strerrno(). 52 | */ 53 | socket_error(); 54 | 55 | socket_error( const std::string& aMessage ) : 56 | std::runtime_error( aMessage ), 57 | #if defined(__linux__) || defined(__APPLE__) 58 | error_code( errno ) 59 | #elif defined(_WIN32) 60 | error_code( WSAGetLastError() ) 61 | #endif 62 | {} 63 | 64 | socket_error( const std::string& aMessage, int aError ) : 65 | std::runtime_error( aMessage ), 66 | error_code( aError ) 67 | {} 68 | 69 | int error_code; 70 | }; 71 | 72 | 73 | /** 74 | * Class SockAddr 75 | * is a wrapper for a sock_addr_in. It provides host endian accessors so that 76 | * client code can mostly forget about network endianess. It also provides an 77 | * operator to convert itself directly into a (sockaddr_in*) for use in BSD 78 | * sockets calls. 79 | * 80 | * @see #Cpf which knows how to serialize and deserialize this for its 81 | * own needs, and on the wire it is called a "SockAddr Info Item". 82 | */ 83 | class SockAddr 84 | { 85 | public: 86 | SockAddr( 87 | unsigned aPort = 0, 88 | unsigned aIP = INADDR_ANY // INADDR_ANY is zero 89 | ); 90 | 91 | SockAddr( const sockaddr_in& aSockAddr ) : 92 | sa( aSockAddr ) 93 | {} 94 | 95 | SockAddr( const char* aNameOrIPAddr, unsigned aPort ); 96 | 97 | void Clear() { memset( &sa, 0, sizeof sa ); } 98 | 99 | /// assign from a sockaddr_in to this 100 | SockAddr& operator=( const sockaddr_in& rhs ) 101 | { 102 | sa = rhs; 103 | return *this; 104 | } 105 | 106 | bool operator==( const SockAddr& other ) const 107 | { 108 | return sa.sin_addr.s_addr == other.sa.sin_addr.s_addr 109 | && sa.sin_port == other.sa.sin_port; 110 | } 111 | 112 | bool operator!=( const SockAddr& other ) const { return !(*this == other); } 113 | 114 | operator const sockaddr_in& () const { return sa; } 115 | operator const sockaddr* () const { return (sockaddr*) &sa; } 116 | operator sockaddr* () const { return (sockaddr*) &sa; } 117 | 118 | // All accessors take and return host endian values. Internally, 119 | // sin_port and sin_addr.s_addr are stored in network byte order (big endian). 120 | 121 | SockAddr& SetFamily( unsigned aFamily ) { sa.sin_family = aFamily; return *this; } 122 | SockAddr& SetPort( unsigned aPort ) { sa.sin_port = htons( aPort ); return *this; } 123 | SockAddr& SetAddr( unsigned aIPAddr ) { sa.sin_addr.s_addr = htonl( aIPAddr ); return *this; } 124 | 125 | unsigned Family() const { return sa.sin_family; } 126 | unsigned Port() const { return ntohs( sa.sin_port ); } 127 | unsigned Addr() const { return ntohl( sa.sin_addr.s_addr ); } 128 | 129 | std::string AddrStr() const { return IpAddrStr( sa.sin_addr ); } 130 | 131 | /** 132 | * Function IsValid 133 | * checks fields according to CIP Vol2 3-3.9.4 134 | * and returns true if valid, else false. 135 | */ 136 | bool IsValid() const 137 | { 138 | return sa.sin_family == AF_INET 139 | && !memcmp( &sa.sin_zero[0], &sa.sin_zero[1], 7 ) 140 | && !sa.sin_zero[0]; 141 | } 142 | 143 | bool IsMulticast() const 144 | { 145 | return (0xf0000000 & Addr()) == 0xe0000000; 146 | // Vol2 3-5.3 147 | // https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml 148 | // "The multicast addresses are in the range 224.0.0.0 through 239.255.255.255." 149 | } 150 | 151 | std::string Format() const; 152 | 153 | protected: 154 | sockaddr_in sa; 155 | }; 156 | 157 | #endif // CIPSTER_SOCKADDR_H_ 158 | -------------------------------------------------------------------------------- /source/src/g_data.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "cip/cipcommon.h" 4 | #include "cip/cipconnection.h" 5 | #include "enet_encap/encap.h" 6 | 7 | int g_CIPSTER_TRACE_LEVEL = CIPSTER_TRACE_LEVEL; 8 | 9 | int g_my_io_udp_port = kEIP_IoUdpPort; 10 | //int g_my_io_udp_port = 2200; 11 | 12 | 13 | /// If this is changed from kEIP_Reserved_Port, then there will be another 14 | /// set of TCP and UDP ports for Encapsulation protocol, but TCP and UDP listeners 15 | /// established, while still preserving the two on kEIP_Reserved_Port. 16 | // int g_my_enip_port = kEIP_Reserved_Port; not yet 17 | 18 | 19 | uint32_t g_run_idle_state; 20 | -------------------------------------------------------------------------------- /source/src/trace.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * 4 | ******************************************************************************/ 5 | #ifndef CIPSTER_TRACE_H_ 6 | #define CIPSTER_TRACE_H_ 7 | 8 | #include 9 | 10 | 11 | /** @file trace.h 12 | * @brief Tracing infrastructure for CIPster 13 | */ 14 | 15 | 16 | /** 17 | * CIPSTER_TRACE_LEVEL_ERROR Enable tracing of error messages. This is the 18 | * default if no trace level is given. 19 | */ 20 | #define CIPSTER_TRACE_LEVEL_ERROR (1<<0) 21 | 22 | /// @def CIPSTER_TRACE_LEVEL_WARNING Enable tracing of warning messages 23 | #define CIPSTER_TRACE_LEVEL_WARNING (1<<1) 24 | 25 | /// @def CIPSTER_TRACE_LEVEL_WARNING Enable tracing of state messages 26 | #define CIPSTER_TRACE_LEVEL_STATE (1<<2) 27 | 28 | /// @def CIPSTER_TRACE_LEVEL_INFO Enable tracing of info messages 29 | #define CIPSTER_TRACE_LEVEL_INFO (1<<3) 30 | 31 | 32 | extern int g_CIPSTER_TRACE_LEVEL; // defined in g_data.cc 33 | 34 | 35 | #ifdef CIPSTER_WITH_TRACES 36 | 37 | #ifndef CIPSTER_TRACE_LEVEL 38 | 39 | #if !defined(__GNUG__) 40 | #pragma message( "CIPSTER_TRACE_LEVEL was not defined setting it to CIPSTER_TRACE_LEVEL_ERROR" ) 41 | #else 42 | #warning CIPSTER_TRACE_LEVEL was not defined setting it to CIPSTER_TRACE_LEVEL_ERROR 43 | #endif 44 | 45 | #define CIPSTER_TRACE_LEVEL CIPSTER_TRACE_LEVEL_ERROR 46 | #endif 47 | 48 | // @def CIPSTER_TRACE_ENABLED Can be used for conditional code compilation 49 | #define CIPSTER_TRACE_ENABLED 50 | 51 | /** @def CIPSTER_TRACE_ERR(...) Trace error messages. 52 | * In order to activate this trace level set the CIPSTER_TRACE_LEVEL_ERROR flag 53 | * in CIPSTER_TRACE_LEVEL. 54 | */ 55 | #define CIPSTER_TRACE_ERR(...) \ 56 | do { \ 57 | if( CIPSTER_TRACE_LEVEL_ERROR & g_CIPSTER_TRACE_LEVEL ) \ 58 | LOG_TRACE(__VA_ARGS__); \ 59 | } while (0) 60 | 61 | /** @def CIPSTER_TRACE_WARN(...) Trace warning messages. 62 | * In order to activate this trace level set the CIPSTER_TRACE_LEVEL_WARNING 63 | * flag in CIPSTER_TRACE_LEVEL. 64 | */ 65 | #define CIPSTER_TRACE_WARN(...) \ 66 | do { \ 67 | if( CIPSTER_TRACE_LEVEL_WARNING & g_CIPSTER_TRACE_LEVEL ) \ 68 | LOG_TRACE(__VA_ARGS__); \ 69 | } while (0) 70 | 71 | /** @def CIPSTER_TRACE_STATE(...) Trace state messages. 72 | * In order to activate this trace level set the CIPSTER_TRACE_LEVEL_STATE flag 73 | * in CIPSTER_TRACE_LEVEL. 74 | */ 75 | #define CIPSTER_TRACE_STATE(...) \ 76 | do { \ 77 | if( CIPSTER_TRACE_LEVEL_STATE & g_CIPSTER_TRACE_LEVEL ) \ 78 | LOG_TRACE(__VA_ARGS__); \ 79 | } while (0) 80 | 81 | /** @def CIPSTER_TRACE_INFO(...) Trace information messages. 82 | * In order to activate this trace level set the CIPSTER_TRACE_LEVEL_INFO flag 83 | * in CIPSTER_TRACE_LEVEL. 84 | */ 85 | #define CIPSTER_TRACE_INFO(...) \ 86 | do { \ 87 | if( CIPSTER_TRACE_LEVEL_INFO & g_CIPSTER_TRACE_LEVEL ) \ 88 | LOG_TRACE(__VA_ARGS__); \ 89 | } while (0) 90 | 91 | #else // define the tracing macros empty in order to save space 92 | 93 | #undef CIPSTER_TRACE_LEVEL 94 | #define CIPSTER_TRACE_LEVEL 0 95 | 96 | #define CIPSTER_TRACE_ERR(...) 97 | #define CIPSTER_TRACE_WARN(...) 98 | #define CIPSTER_TRACE_STATE(...) 99 | #define CIPSTER_TRACE_INFO(...) 100 | #endif 101 | 102 | #endif //CIPSTER_TRACE_H_ 103 | -------------------------------------------------------------------------------- /source/src/typedefs.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * 4 | ******************************************************************************/ 5 | #ifndef CIPSTER_TYPEDEFS_H_ 6 | #define CIPSTER_TYPEDEFS_H_ 7 | 8 | #include 9 | #include 10 | 11 | 12 | /** @brief Data types as defined in the CIP Specification Vol 1 Appendix C 13 | */ 14 | typedef uint8_t CipOctet; ///< 8 bit value that indicates particular data type 15 | typedef uint8_t CipBool; ///< Boolean data type 16 | typedef uint8_t CipByte; ///< 8-bit bit string 17 | typedef uint16_t CipWord; ///< 16-bit bit string 18 | typedef uint32_t CipDword; ///< 32-bit bit string 19 | typedef uint8_t CipUsint; ///< 8-bit unsigned 20 | typedef uint16_t CipUint; ///< CipUint 16-bit unsigned 21 | typedef uint32_t CipUdint; ///< CipUdint 32-bit unsigned 22 | typedef int8_t CipSint; ///< 8-bit signed integer 23 | typedef int16_t CipInt; ///< 16-bit signed integer 24 | typedef int32_t CipDint; ///< 32-bit signed integer 25 | typedef float CipReal; ///< 32-bit IEEE 754 floating point 26 | typedef double CipLreal; ///< 64-bit IEEE 754 floating point 27 | 28 | typedef int64_t CipLint; ///< 64-bit signed integer 29 | typedef uint64_t CipUlint; ///< 64-bit unsignedeger 30 | typedef uint64_t CipLword; ///< 64-bit bit string 31 | 32 | 33 | 34 | /// Constant identifying an invalid socket 35 | const int kSocketInvalid = -1; 36 | 37 | /** 38 | * 39 | * The following are generally true regarding return status: 40 | * -1 ... an error occurred 41 | * 0 ... success 42 | * 43 | * Occasionally there is a variation on this: 44 | * -1 ... an error occurred 45 | * 0 .. success and there is no reply to send 46 | * 1 ... success and there is a reply to send 47 | * 48 | * For both of these cases EipStatus is the return type. 49 | * 50 | * Other return type are: 51 | * -- return pointer to thing, 0 if error (return type is "pointer to thing") 52 | * -- return count of something, -1 if error, (return type is int) 53 | * 54 | */ 55 | 56 | 57 | /** 58 | * Enum EipStatus 59 | * is a set of status values which are returned by API functions. 60 | */ 61 | enum EipStatus 62 | { 63 | kEipStatusOk = 0, 64 | kEipStatusOkSend = 1, 65 | kEipStatusError = -1, 66 | }; 67 | 68 | 69 | /// The count of elements in a single dimension array: 70 | #define DIM(x) int( sizeof(x)/sizeof((x)[0]) ) 71 | #define UDIM(x) unsigned( sizeof(x)/sizeof((x)[0]) ) 72 | 73 | #endif // CIPSTER_TYPEDEFS_H_ 74 | -------------------------------------------------------------------------------- /source/src/utils/random.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * random.cpp 3 | * 4 | * Created on: Dec 16, 2013 5 | * Author: mmm 6 | */ 7 | 8 | #include "random.h" 9 | #include 10 | 11 | 12 | Random* RandomNew( SetSeed set_seed, GetNextUInt32 get_next_uint32 ) 13 | { 14 | Random* out = (Random*) malloc( sizeof(Random) ); 15 | 16 | out->set_seed = set_seed; 17 | out->get_next_uint32 = get_next_uint32; 18 | 19 | return out; 20 | } 21 | -------------------------------------------------------------------------------- /source/src/utils/random.h: -------------------------------------------------------------------------------- 1 | /* 2 | * random.h 3 | * 4 | * Created on: Dec 1, 2013 5 | * Author: mmm 6 | */ 7 | 8 | #ifndef CIPSTER_RANDOM_H_ 9 | #define CIPSTER_RANDOM_H_ 10 | 11 | #include 12 | 13 | typedef void (* SetSeed)( uint32_t seed ); 14 | typedef uint32_t (* GetNextUInt32)( void ); 15 | 16 | typedef struct 17 | { 18 | uint32_t current_seed_value; ///< Holds the current seed/random value 19 | SetSeed set_seed; ///< Function pointer to SetSeed function 20 | GetNextUInt32 get_next_uint32; ///< Function pointer to GetNextUInt32 function 21 | } Random; 22 | 23 | Random* RandomNew( SetSeed, GetNextUInt32 ); 24 | 25 | #endif // CIPSTER_RANDOM_H_ 26 | -------------------------------------------------------------------------------- /source/src/utils/strprint.cc: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * Copyright (c) 2016-2018, SoftPLC Corporation. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | 9 | #include "../cip/cipcommon.h" 10 | 11 | 12 | static int vprint( std::string* result, const char* format, va_list ap ) 13 | { 14 | char msg[512]; 15 | size_t len = vsnprintf( msg, sizeof(msg), format, ap ); 16 | 17 | if( len < sizeof(msg) ) // the output fit into msg 18 | { 19 | result->append( msg, msg + len ); 20 | return len; 21 | } 22 | else 23 | return 0; // increase size of msg[], or shorten prints 24 | } 25 | 26 | 27 | int StrPrintf( std::string* aResult, const char* aFormat, ... ) 28 | { 29 | va_list args; 30 | 31 | va_start( args, aFormat ); 32 | int ret = vprint( aResult, aFormat, args ); 33 | va_end( args ); 34 | 35 | return ret; 36 | } 37 | 38 | 39 | std::string StrPrintf( const char* aFormat, ... ) 40 | { 41 | std::string ret; 42 | va_list args; 43 | 44 | va_start( args, aFormat ); 45 | int ignore = vprint( &ret, aFormat, args ); 46 | (void) ignore; 47 | va_end( args ); 48 | 49 | return ret; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /source/src/utils/xorshiftrandom.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * xorshiftrandom.c 3 | * 4 | * Created on: Nov 28, 2013 5 | * Author: mmm 6 | */ 7 | 8 | #include "xorshiftrandom.h" 9 | 10 | static uint32_t xor_shift_seed; //* < File-global variable holding the current seed 11 | 12 | void SetXorShiftSeed(uint32_t seed) { 13 | xor_shift_seed = seed; 14 | } 15 | 16 | /** @brief Pseudo-random number algorithm 17 | * The algorithm used to create the pseudo-random numbers. 18 | * Works directly on the file global variable 19 | */ 20 | void CalculateNextSeed(void) { 21 | xor_shift_seed ^= xor_shift_seed << 13; 22 | xor_shift_seed ^= xor_shift_seed >> 17; 23 | xor_shift_seed ^= xor_shift_seed << 5; 24 | } 25 | 26 | uint32_t NextXorShiftUint32(void) { 27 | CalculateNextSeed(); 28 | return xor_shift_seed; 29 | } 30 | -------------------------------------------------------------------------------- /source/src/utils/xorshiftrandom.h: -------------------------------------------------------------------------------- 1 | /* 2 | * xorshiftrandom.h 3 | * 4 | * Created on: Dec 1, 2013 5 | * Author: mmm 6 | */ 7 | 8 | /** 9 | * @file xorshiftrandom.h 10 | * 11 | * The public interface of the XOR shift pseudo-random number generator 12 | */ 13 | #include 14 | 15 | #ifndef CIPSTER_XORSHIFTRANDOM_H_ 16 | #define CIPSTER_XORSHIFTRANDOM_H_ 17 | 18 | /** 19 | * Function SetXorShiftSeed 20 | * sets the initial seed for the XOR shift pseudo-random algorithm 21 | * @param aSeed The initial seed value 22 | */ 23 | void SetXorShiftSeed( uint32_t aSeed ); 24 | 25 | /** 26 | * Function NextXorShiftUint32 27 | * returns the next generated pseudo-random number 28 | * @return uint32_t - The next pseudo-random number 29 | */ 30 | uint32_t NextXorShiftUint32(); 31 | 32 | #endif // CIPSTER__XORSHIFTRANDOM_H_ 33 | -------------------------------------------------------------------------------- /source/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Add test subdirectories # 3 | ####################################### 4 | 5 | ####################################### 6 | # Add test includes # 7 | ####################################### 8 | add_test_includes() 9 | 10 | 11 | ################################################### 12 | # Copy custom test output file to binary location # 13 | ################################################### 14 | configure_file( CTestCustom.cmake ${PROJECT_BINARY_DIR}/CTestCustom.cmake ) 15 | 16 | add_subdirectory( utils ) 17 | add_subdirectory( enet_encap ) 18 | add_executable( CIPster_Tests CIPsterTests.cpp ) 19 | 20 | find_library ( CPPUTEST_LIBRARY CppUTest ${CPPUTEST_HOME}/cpputest_build/lib ) 21 | find_library ( CPPUTESTEXT_LIBRARY CppUTestExt ${CPPUTEST_HOME}/cpputest_build/lib ) 22 | 23 | target_link_libraries( CIPster_Tests gcov ${CPPUTEST_LIBRARY} ${CPPUTESTEXT_LIBRARY} ) 24 | target_link_libraries( CIPster_Tests UtilsTest Utils ) 25 | target_link_libraries( CIPster_Tests EthernetEncapsulationTest ENET_ENCAP ) 26 | 27 | ######################################## 28 | # Adds test to CTest environment # 29 | ######################################## 30 | add_test(CIPster_Tests ${EXECUTABLE_OUTPUT_PATH}/CIPster_Tests) 31 | -------------------------------------------------------------------------------- /source/tests/CTestCustom.cmake: -------------------------------------------------------------------------------- 1 | set( CTEST_CUSTOM_POST_TEST ${CTEST_CUSTOM_POST_TEST} "cat Testing/Temporary/LastTest.log" ) 2 | -------------------------------------------------------------------------------- /source/tests/OpENerTests.cpp: -------------------------------------------------------------------------------- 1 | #include "CIPsterTests.h" 2 | 3 | int main(int argc, char** argv) 4 | { 5 | /* These checks are here to make sure assertions outside test runs don't crash */ 6 | CHECK(true); 7 | LONGS_EQUAL(1, 1); 8 | 9 | return CommandLineTestRunner::RunAllTests(argc, argv); 10 | } 11 | -------------------------------------------------------------------------------- /source/tests/OpENerTests.h: -------------------------------------------------------------------------------- 1 | #include "CppUTest/CommandLineTestRunner.h" 2 | 3 | IMPORT_TEST_GROUP(RandomClass); 4 | IMPORT_TEST_GROUP(XorShiftRandom); 5 | IMPORT_TEST_GROUP(EndianConversion); 6 | -------------------------------------------------------------------------------- /source/tests/enet_encap/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | opener_common_includes() 3 | 4 | set( EthernetEncapsulationTestSrc endianconvtest.cpp ) 5 | 6 | include_directories( ${SRC_DIR}/enet_encap ) 7 | 8 | add_library( EthernetEncapsulationTest ${EthernetEncapsulationTestSrc} ) 9 | -------------------------------------------------------------------------------- /source/tests/enet_encap/endianconvtest.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * 4 | ******************************************************************************/ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "endianconv.h" 14 | 15 | #include "ciptypes.h" 16 | 17 | 18 | TEST_GROUP( EndianConversion ) 19 | { 20 | }; 21 | 22 | 23 | TEST( EndianConversion, GetIntFromMessage ) 24 | { 25 | CipOctet test_message[] = { 8, 60 }; 26 | CipOctet* message = test_message; 27 | uint16_t returned_value = GetIntFromMessage( &message ); 28 | 29 | LONGS_EQUAL( 15368, returned_value ); 30 | POINTERS_EQUAL( test_message + 2, message ); 31 | } 32 | 33 | 34 | TEST( EndianConversion, GetDintFromMessage ) 35 | { 36 | CipOctet test_message[] = { 28, 53, 41, 37 }; 37 | CipOctet* message = test_message; 38 | uint32_t returned_value = GetDintFromMessage( &message ); 39 | 40 | LONGS_EQUAL( 623457564, returned_value ); 41 | POINTERS_EQUAL( test_message + 4, message ); 42 | } 43 | 44 | 45 | TEST( EndianConversion, GetLintFromMessage ) 46 | { 47 | CipOctet test_message[] = { 81, 126, 166, 15, 70, 97, 208, 236 }; 48 | CipOctet* message = test_message; 49 | uint64_t returned_value = GetLintFromMessage( &message ); 50 | 51 | LONGS_EQUAL( 5872313548673241324, returned_value ); 52 | POINTERS_EQUAL( test_message + 8, message ); 53 | } 54 | 55 | 56 | TEST( EndianConversion, AddIntToMessage ) 57 | { 58 | CipUint value_to_add_to_message = 0x5499; 59 | CipOctet message[2]; 60 | CipOctet* message_pointer = message; 61 | 62 | AddIntToMessage( value_to_add_to_message, &message_pointer ); 63 | 64 | BYTES_EQUAL( 0x99, message[0] ); 65 | BYTES_EQUAL( 0x54, message[1] ); 66 | 67 | POINTERS_EQUAL( message + 2, message_pointer ) 68 | } 69 | 70 | 71 | TEST( EndianConversion, AddDintToMessage ) 72 | { 73 | CipUdint value_to_add_to_message = 0x25E0C459; 74 | CipOctet message[4]; 75 | CipOctet* message_pointer = message; 76 | 77 | AddDintToMessage( value_to_add_to_message, &message_pointer ); 78 | 79 | BYTES_EQUAL( 0x59, message[0] ); 80 | BYTES_EQUAL( 0xC4, message[1] ); 81 | BYTES_EQUAL( 0xE0, message[2] ); 82 | BYTES_EQUAL( 0x25, message[3] ); 83 | 84 | POINTERS_EQUAL( message + 4, message_pointer ) 85 | } 86 | 87 | 88 | TEST( EndianConversion, AddLintToMessage ) 89 | { 90 | CipLint value_to_add_to_message = 0x2D2AEF0B84095230; 91 | CipOctet message[8]; 92 | CipOctet* message_pointer = message; 93 | 94 | AddLintToMessage( value_to_add_to_message, &message_pointer ); 95 | 96 | /* Expected message from highest to lowest byte [30][52][09][84][0B][EF][2A][2D] */ 97 | BYTES_EQUAL( 0x2D, message[0] ); 98 | BYTES_EQUAL( 0x2A, message[1] ); 99 | BYTES_EQUAL( 0xEF, message[2] ); 100 | BYTES_EQUAL( 0x0B, message[3] ); 101 | BYTES_EQUAL( 0x84, message[4] ); 102 | BYTES_EQUAL( 0x09, message[5] ); 103 | BYTES_EQUAL( 0x52, message[6] ); 104 | BYTES_EQUAL( 0x30, message[7] ); 105 | 106 | POINTERS_EQUAL( message + 8, message_pointer ) 107 | } 108 | 109 | 110 | TEST( EndianConversion, EncapsulateIpAddress ) 111 | { 112 | CipOctet ip_message[8]; 113 | CipOctet* ip_message_ponter = ip_message; 114 | 115 | DetermineEndianess(); 116 | 117 | EncapsulateIpAddress( 0xAF12, 0x25E0C459, &ip_message_ponter ); 118 | 119 | BYTES_EQUAL( AF_INET >> 8, ip_message[0] ); 120 | BYTES_EQUAL( AF_INET, ip_message[1] ); 121 | BYTES_EQUAL( 0x12, ip_message[2] ); 122 | BYTES_EQUAL( 0xAF, ip_message[3] ); 123 | BYTES_EQUAL( 0x59, ip_message[4] ); 124 | BYTES_EQUAL( 0xC4, ip_message[5] ); 125 | BYTES_EQUAL( 0xE0, ip_message[6] ); 126 | BYTES_EQUAL( 0x25, ip_message[7] ); 127 | } 128 | 129 | TEST( EndianConversion, MoveMessageNOctets ) { 130 | CipOctet message[8]; 131 | CipOctet* message_runner = message; 132 | 133 | MoveMessageNOctets( 4, &message_runner ); 134 | 135 | POINTERS_EQUAL( message + 4, message_runner ); 136 | } 137 | 138 | TEST( EndianConversion, FillNextNMEssageOctetsWith ) { 139 | CipOctet message[8]; 140 | CipOctet* message_runner = message; 141 | 142 | memset( message, 15, 8 ); 143 | 144 | FillNextNMessageOctetsWith( 0, 8, &message_runner ); 145 | BYTES_EQUAL( 0, message_runner[0] ); 146 | BYTES_EQUAL( 0, message_runner[1] ); 147 | BYTES_EQUAL( 0, message_runner[2] ); 148 | BYTES_EQUAL( 0, message_runner[3] ); 149 | BYTES_EQUAL( 0, message_runner[4] ); 150 | BYTES_EQUAL( 0, message_runner[5] ); 151 | BYTES_EQUAL( 0, message_runner[6] ); 152 | BYTES_EQUAL( 0, message_runner[7] ); 153 | } 154 | -------------------------------------------------------------------------------- /source/tests/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | opener_common_includes() 3 | 4 | set( UtilsTestSrc randomTests.cpp xorshiftrandomtests.cpp) 5 | 6 | include_directories( ${SRC_DIR}/utils ) 7 | 8 | add_library( UtilsTest ${UtilsTestSrc} ) 9 | -------------------------------------------------------------------------------- /source/tests/utils/randomTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * randomTests.cpp 3 | * 4 | * Created on: Dec 16, 2013 5 | * Author: mmm 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | #include 13 | #include 14 | } 15 | 16 | TEST_GROUP(RandomClass) 17 | { 18 | 19 | }; 20 | 21 | TEST(RandomClass, CreateXOrShiftObject) 22 | { 23 | Random* pRandom; 24 | uint32_t nResult = 0; 25 | pRandom = RandomNew(SetXorShiftSeed, NextXorShiftUint32); 26 | POINTERS_EQUAL(SetXorShiftSeed, pRandom->set_seed); 27 | POINTERS_EQUAL(NextXorShiftUint32, pRandom->get_next_uint32); 28 | } 29 | -------------------------------------------------------------------------------- /source/tests/utils/xorshiftrandomtests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * xorshiftrandomtests.c 3 | * 4 | * Created on: Dec 1, 2013 5 | * Author: mmm 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | #include 13 | } 14 | 15 | TEST_GROUP(XorShiftRandom) 16 | { 17 | 18 | }; 19 | 20 | /*This test should always return 0 as the next random number (see XorShift algorithm*/ 21 | TEST(XorShiftRandom, SeedZeroInitResult) 22 | { 23 | uint32_t nResult; 24 | nResult = 1; 25 | SetXorShiftSeed(0); 26 | nResult = NextXorShiftUint32(); 27 | LONGS_EQUAL(0, nResult); 28 | } 29 | 30 | /*Characterization test*/ 31 | TEST(XorShiftRandom, SeedOneCharacterization) 32 | { 33 | uint32_t nResult; 34 | SetXorShiftSeed(1); 35 | nResult = NextXorShiftUint32(); 36 | LONGS_EQUAL(270369, nResult); 37 | nResult = NextXorShiftUint32(); 38 | LONGS_EQUAL(67634689, nResult); 39 | nResult = NextXorShiftUint32(); 40 | LONGS_EQUAL(2647435461, nResult); 41 | nResult = NextXorShiftUint32(); 42 | LONGS_EQUAL(307599695, nResult); 43 | } 44 | --------------------------------------------------------------------------------