diff options
153 files changed, 15406 insertions, 1966 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23330ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,112 @@ +/CMakeFiles +/build*/* +/examples/hello_world/build +/.settings +/doc +/daemon/CMakeFiles +/examples/CMakeFiles +/implementation/configuration/include/internal.hpp +/test/application_tests/application_test.json +/test/application_tests/application_test_daemon.json +/test/application_tests/application_test_no_dispatch_threads.json +/test/application_tests/application_test_no_dispatch_threads_daemon.json +/test/big_payload_tests/big_payload_test_tcp_client.json +/test/big_payload_tests/big_payload_test_tcp_service.json +/test/big_payload_tests/big_payload_test_tcp_client_random.json +/test/big_payload_tests/big_payload_test_tcp_service_random.json +/test/big_payload_tests/big_payload_test_tcp_client_limited_general.json +/test/big_payload_tests/big_payload_test_tcp_service_limited_general.json +/test/big_payload_tests/big_payload_test_tcp_client_queue_limited_general.json +/test/big_payload_tests/big_payload_test_tcp_client_queue_limited_specific.json +/test/big_payload_tests/big_payload_test_tcp_service_queue_limited_general.json +/test/big_payload_tests/big_payload_test_tcp_service_queue_limited_specific.json +/test/magic_cookies_tests/magic_cookies_test_client.json +/test/magic_cookies_tests/magic_cookies_test_service.json +/test/payload_tests/external_local_payload_test_client_external.json +/test/payload_tests/external_local_payload_test_client_local.json +/test/payload_tests/external_local_payload_test_service.json +/test/routing_tests/external_local_routing_test_client_external.json +/test/routing_tests/external_local_routing_test_service.json +/test/routing_tests/local_routing_test_starter.sh +/test/client_id_tests/client_id_test_diff_client_ids_diff_ports_master.json +/test/client_id_tests/client_id_test_diff_client_ids_diff_ports_slave.json +/test/client_id_tests/client_id_test_diff_client_ids_same_ports_master.json +/test/client_id_tests/client_id_test_diff_client_ids_same_ports_slave.json +/test/client_id_tests/client_id_test_diff_client_ids_partial_same_ports_master.json +/test/client_id_tests/client_id_test_diff_client_ids_partial_same_ports_slave.json +/test/client_id_tests/client_id_test_same_client_ids_diff_ports_master.json +/test/client_id_tests/client_id_test_same_client_ids_diff_ports_slave.json +/test/client_id_tests/client_id_test_same_client_ids_same_ports_master.json +/test/client_id_tests/client_id_test_same_client_ids_same_ports_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_master.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_same_ports_master.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_same_ports_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_partial_same_ports_master.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_partial_same_ports_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_same_client_ids_diff_ports_master.json +/test/subscribe_notify_tests/subscribe_notify_test_same_client_ids_diff_ports_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_same_client_ids_same_ports_master.json +/test/subscribe_notify_tests/subscribe_notify_test_same_client_ids_same_ports_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_one_event_two_eventgroups_master.json +/test/subscribe_notify_tests/subscribe_notify_test_one_event_two_eventgroups_udp_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_one_event_two_eventgroups_tcp_slave.json +/test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_master.json +/test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_slave.json +/test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_master_tcp.json +/test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_slave_tcp.json +/test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_master_udp.json +/test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_slave_udp.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_master.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_master_tcp.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_slave_tcp.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_same_ports_master_tcp.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_same_ports_slave_tcp.json +/test/cpu_load_tests/cpu_load_test_client_slave.json +/test/cpu_load_tests/cpu_load_test_client_master.json +/test/cpu_load_tests/cpu_load_test_service_slave.json +/test/cpu_load_tests/cpu_load_test_service_master.json +/tools/CMakeFiles +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_same_service_id_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_same_service_id_slave.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_slave.json +/test/initial_event_tests/initial_event_test_diff_client_ids_partial_same_ports_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_partial_same_ports_slave.json +/test/initial_event_tests/initial_event_test_diff_client_ids_same_ports_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_same_ports_slave.json +/test/initial_event_tests/initial_event_test_same_client_ids_diff_ports_master.json +/test/initial_event_tests/initial_event_test_same_client_ids_diff_ports_slave.json +/test/initial_event_tests/initial_event_test_same_client_ids_same_ports_master.json +/test/initial_event_tests/initial_event_test_same_client_ids_same_ports_slave.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_master_tcp.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_slave_tcp.json +/test/initial_event_tests/initial_event_test_diff_client_ids_same_ports_master_tcp.json +/test/initial_event_tests/initial_event_test_diff_client_ids_same_ports_slave_tcp.json +/test/offer_tests/offer_test_external_master.json +/test/offer_tests/offer_test_external_slave.json +/test/offer_tests/offer_test_external_master_starter.sh +/test/offer_tests/offer_test_big_sd_msg_master_starter.sh +/test/offer_tests/offer_test_big_sd_msg_master.json +/test/offer_tests/offer_test_big_sd_msg_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_master.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_slave.json +test/security_tests/security_test_config_client_external_allow.json +test/security_tests/security_test_config_client_external_deny.json +test/security_tests/security_test_config_service_external_allow.json +test/security_tests/security_test_config_service_external_deny.json +test/security_tests/security_test_local_config.json +/test/pending_subscription_tests/pending_subscription_test_master.json +/test/pending_subscription_tests/pending_subscription_test_master_starter.sh +/test/malicious_data_tests/malicious_data_test_master.json +/test/malicious_data_tests/malicious_data_test_master_starter.sh +/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_master.json +/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_slave.json +/test/security_config_plugin_tests/security_config_plugin_test_local.json +/test/e2e_tests/e2e_test_client_external.json +/test/e2e_tests/e2e_test_service_external.json +/test/event_tests/event_test_master.json +/test/event_tests/event_test_slave_tcp.json +/test/event_tests/event_test_slave_udp.json + @@ -1,6 +1,145 @@ Changes ======= +v2.14.16 +- Ensure restarting of TCP connection if TCP reset is received + before 3 way handshake is finished +- Reworked IPSec connection unblocking if reboot was detected + +v2.14.15 +- Ensure that all clients receive the security policy update + +v2.14.14 +- Increased the max allowed number of open file descriptors + from the soft limit to the hard limit for vsomeipd +- Improved handling for EMFILE error (per-process limit of open + filedescriptors reached) +- Fixed client ID assignment + +v2.14.13 +- Fixed race condition leading to unintended sending + of selective broadcasts. + +v2.14.12 +- Make timeout during application shutdown configurable via + "shutdown_timeout" json configuration file parameter +- Fix bug leading to too fast reassignment of client IDs acquired via + auto-configuration in conjunction with usage of a non-continuous + diagnosis mask + +v2.14.11 +- Fixed race condition when opening shared memory +- Fixed race condition when inserting SubscribeEventGroupACK + entries +- Added prefix for security related log messages + +v2.14.10 +- Fixed race condition in client ID to UID mapping. + +v2.14.9 +- Fixed race condition in event / response validation + and client ID to UID mapping removal +- Limit reconnect attempts of local client endpoints +- Increase robustness of local server endpoint + in case of corrupted data stream + +v2.14.8 +- Handle ANY_METHOD for security policy updates + +v2.14.7 +- Reverted security related feature if same client connects + again with different credentials. + +v2.14.6 +- Fixed issue related to client ID reuse if security is activated +- Use chmod instead of umask to ensure correct permissions of unix + domain sockets + +v2.14.5 +- Fixed race condition when handling security policy updates + +v2.14.4 +- Added whitelist feature for security policy update / removal +- Added / improved security related checks + +v2.14.3 +- Prevent concurrent access to any client policies + +v2.14.2 +- Fix possible deadlock when receiving invalid responses from remote + TCP services +- Enabled loading of UID GID specific security configuration during + application startup + +v2.14.1 +- Check header fields in udp endpoints for correct values +- Fixed race condition in event::notify methods +- Ensure to always resume sending if endpoints were restarted +- Prevent possible concurrent access to receive buffer in endpoints +- Allow or deny all remote clients via security config parameter +- Make receive buffer size configurable for UDP client and + server endpoints via "udp-receive-buffer-size" parameter + in json configuration file +- Fix race condition which could lead to premature resubscription if a + remote service was stop offered and offered again + +v2.14.0 +- Introduce security policy updates during runtime and extend + security configuration to filter on instance ID and method ID level + For more information see the vsomeipUserGuide. + +v2.13.2 +- Make I/O thread nice level configurable + +v2.13.1 +- Improve check of subnet mask for remote subscribers +- Restart TCP connections if too big messages are received +- Don't process subscriptions if IPsec connection isn't established +- Additionally check protocol header fields in tcp endpoints as well +- Fix race when expiring remote services + +v2.13.0 +- Fix handling of requests send to the service discovery port +- Log time since routing state was set to RS_RESUMED in cyclic version + logger +- Blame externally offered services if routing state is set to + RS_SUSPENDED +- Prevent possible exception during application shutdown + +v2.12.3 +- Improve handling of broken TCP streams + +v2.12.2 +- Expire remote subscriptions to local services when routing state is set to + suspending + +v2.12.1 +- Fixed race condition in event registration + +v2.12.0 +- Improve magic cookie handling. +- Fix possible deadlock on application shutdown +- Fix handling of local StopOffers when duplicate service instances + are present in the network. +- Make trace connector json configuration more flexible +- Fix bug in security configuration when black listing single + UIDs/GIDs + +v2.11.1 +- Fix possible deadlock on application shutdown + +v2.10.22 +- Handle EPERM error (Operation not permitted) in cei::send_cbk +- Restart TCP endpoint if a maximum number of aborted restarts + (due to state == CONNECTING) is reached or if a maximum allowed time since + the connection attempt was started has elapsed +- Fixed missing call of subscription status handlers + for external fields if a following subscription + was done triggered by a service being stopped/expired +- Fixed crash in vsomeip security credential check +- Added Debian hardening compiler flags (requires GCC >= 5.2) +- Fixed compilation with GCC 8 + v2.10.21 - Improve memory usage of routing manager. - Improve handling of incoming SD messages with uncommon entry @@ -59,8 +198,6 @@ v2.10.14 - Bugfix for pending subscriptions when same port is used for TCP and UDP endpoint option. -v2.10.13 - v2.10.12 - Fix exception handling for boost logger @@ -149,8 +286,6 @@ v2.10.5 v2.10.4 -- Extended diagnosis plugin to handle requests for - "disableRxAndEnableTx" and "disableRxAndTx". - Catch unhandled user code exceptions thrown from called handlers. - Don't send SubscribeEventGroupNACK for pending subscriptions on next offer to reduce the amount of StopSubscribe/Subscribe messages. diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c4b3f5..1d6f6d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ cmake_minimum_required (VERSION 2.8.12) project (vsomeip) set (VSOMEIP_MAJOR_VERSION 2) -set (VSOMEIP_MINOR_VERSION 10) -set (VSOMEIP_PATCH_VERSION 21) +set (VSOMEIP_MINOR_VERSION 14) +set (VSOMEIP_PATCH_VERSION 16) set (VSOMEIP_HOTFIX_VERSION 0) set (VSOMEIP_VERSION ${VSOMEIP_MAJOR_VERSION}.${VSOMEIP_MINOR_VERSION}.${VSOMEIP_PATCH_VERSION}) @@ -65,7 +65,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(EXPORTSYMBOLS "-Wl,-export-dynamic -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/exportmap.gcc") set(NO_DEPRECATED "") set(OPTIMIZE "") - set(OS_CXX_FLAGS "-D_GLIBCXX_USE_NANOSLEEP -pthread -O -Wall -Wextra -Wformat -Wformat-security -Wconversion -fexceptions -fstrict-aliasing -fstack-protector -fasynchronous-unwind-tables -fno-omit-frame-pointer -D_FORTIFY_SOURCE=2") + set(OS_CXX_FLAGS "-D_GLIBCXX_USE_NANOSLEEP -pthread -O -Wall -Wextra -Wformat -Wformat-security -Wconversion -fexceptions -fstrict-aliasing -fstack-protector-strong -fasynchronous-unwind-tables -fno-omit-frame-pointer -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security -fPIE -pie -Wl,-z,relro,-z,now") endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") diff --git a/daemon/vsomeipd.cpp b/daemon/vsomeipd.cpp index b9a27d0..8314b9a 100644 --- a/daemon/vsomeipd.cpp +++ b/daemon/vsomeipd.cpp @@ -10,7 +10,7 @@ #include <thread> #include <condition_variable> #include <mutex> - +#include <cstring> #include <iostream> #include <vsomeip/vsomeip.hpp> @@ -32,6 +32,11 @@ static std::condition_variable_any sighandler_condition; static std::recursive_mutex sighandler_mutex; #endif +#ifndef _WIN32 +#include <sys/time.h> +#include <sys/resource.h> +#endif + #ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING /* * Handle signal to stop the daemon @@ -109,6 +114,18 @@ int vsomeipd_process(bool _is_quiet) { #endif if (its_application->init()) { if (its_application->is_routing()) { +#ifndef _WIN32 + // raise the max number of open file descriptors to the hard limit + struct rlimit its_current_fd_limit; + if (-1 == getrlimit(RLIMIT_NOFILE, &its_current_fd_limit)) { + VSOMEIP_ERROR << "Couldn't read out RLIMIT_NOFILE: " << std::strerror(errno); + } else { + its_current_fd_limit.rlim_cur = its_current_fd_limit.rlim_max; + if (-1 == setrlimit(RLIMIT_NOFILE, &its_current_fd_limit)) { + VSOMEIP_ERROR << "Couldn't set RLIMIT_NOFILE: " << std::strerror(errno); + } + } +#endif its_application->start(); #ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING sighandler_thread.join(); @@ -173,7 +190,7 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } - umask(0); + umask(0111); its_signature = setsid(); if (its_signature < 0) { diff --git a/documentation/vsomeipUserGuide b/documentation/vsomeipUserGuide index 9996063..f4f5770 100644 --- a/documentation/vsomeipUserGuide +++ b/documentation/vsomeipUserGuide @@ -42,7 +42,7 @@ Build Instructions ------------------ Dependencies ~~~~~~~~~~~~ -* A C++11 enabled compiler like gcc >= 4.8 is needed. +* A C++11 enabled compiler like gcc >= 5.2 is needed. * vsomeip uses cmake as buildsystem. * vsomeip uses Boost >= 1.55: ** Ubuntu 14.04: @@ -298,12 +298,20 @@ ID). + * 'diagnosis_mask' + -The diagnosis mask (2 byte) is used to control the amount of bits used for the -diagnosis address in client identifiers. The default value is `0xFF00` meaning +The diagnosis mask (2 byte) is used to control the maximum amount of allowed +concurrent vsomeip clients on an ECU and the start value of the client IDs. ++ +The default value is `0xFF00` meaning the most significant byte of the client ID is reserved for the diagnosis -address. Setting the mask to `0xFE00` allows to only use the 7 most -significant bits of the client ID as a diagnosis address. This can be used to -increase the maximum amount of concurrent active clients on an ECU. +address and the client IDs will start with the diagnosis address as specified. +The maximum number of clients is 255 as the Hamming weight of the inverted mask +is 8 (2^8 = 256 - 1 (for the routing manager) = 255). The resulting client ID +range with a diagnosis address of for example 0x45 would be 0x4501 to 0x45ff. ++ +Setting the mask to `0xFE00` doubles client ID range to 511 clients as the +Hamming weight of the inverted mask is greater by one. With a diagnosis address +of 0x45 the start value of client IDs is 0x4401 as bit 8 in 0x4500 is masked +out. This then yields a client ID range of 0x4400 to 0x45ff. * 'network' + @@ -404,23 +412,26 @@ to DLT. *** 'channel' (optional) + The id of the channel over that the filtered messages are forwarded to DLT. If -no channel is specified the default channel is used. -+ -IMPORTANT: If multiple filters are used, the channel MUST always be -specified and accordingly defined! Each filter needs its own channel! -+ -*** 'services (array)' (optional) +no channel is specified the default channel (TC) is used. If you want to use a +filter in several different channels, you can provide an array of channel ids. + -Contains the service ids. The messages that relates to the specified services will be filtered. +NOTE: If you use a positive filter with multiple channels, the same message +will be forwared multiple times to DLT. + -*** 'methods (array)' (optional) +*** 'matches' (optional) + -Contains the method ids. The messages that relates to the specified methods will be filtered. +Specification of the criteria to include/exclude a message into/from the trace. +You can either specify lists (array) or ranges of matching elements. + -*** 'clients (array)' (optional) +A list may contain single identifiers which match all messages from/to all +instances of the corresponding service or tuples consisting of service-, +instance- and method-identifier. 'any' may be used as a wildcard for matching +all services, instances or methods. + -Contains the client ids. The messages that relates to the specified clients will -be filtered. +A range is specified by two tuples "from" and "to", each consisting of +service-, instance-and method-identifier. All messages with service-, +instance-and method-identifiers that are greater than or equal to "from" +and less than or equal to "to" are matched. + *** 'type' (optional) + @@ -461,6 +472,11 @@ callbacks if max_dispatchers is configured greater than 0). The number of internal threads to process messages and events within an application. Valid values are 1-255. Default is 2. + +** 'io_thread_nice' (optional) ++ +The nice level for internal threads processing messages and events. POSIX/Linux only. +For actual values refer to nice() documentation. ++ ** 'request_debounce_time' (optional) + Specifies a debounce-time interval in ms in which request-service messages are sent to @@ -743,14 +759,29 @@ allocated buffer used to process them before the memory for the buffer is released and starts to grow dynamically again. This setting can be useful in scenarios where only a small number of the overall messages are a lot bigger then the rest and the memory allocated to process them should be released in a -timely manner. If not specified via this setting the buffer sizes by default -aren't reseted and are as big as the biggest processed message. +timely manner. If the value is set to zero the buffer sizes aren't reseted and +are as big as the biggest processed message. (default is 5) + Example: `buffer-shrink-threshold` is set to 50. A message with 500 bytes has to be processed and the buffers grow accordingly. After this message 50 consecutive messages smaller than 250 bytes have to be processed before the buffer size is reduced and starts to grow dynamically again. +* `tcp-restart-aborts-max` ++ +Setting to limit the number of TCP client endpoint restart aborts due to unfinished TCP handshake. +After the limit is reached, a forced restart of the TCP client endpoint is done if the connection attempt is still pending. + +* `tcp-connect-time-max` ++ +Setting to define the maximum time until the TCP client endpoint connection attempt should be finished. +If `tcp-connect-time-max` is elapsed, the TCP client endpoint is forcely restarted if the connection attempt is still pending. + +* `udp-receive-buffer-size` ++ +Specifies the size of the socket receive buffer (`SO_RCVBUF`) used for +UDP client and server endpoints in bytes. (default: 1703936) + * `internal_services` (optional array) + Specifies service/instance ranges for pure internal service-instances. @@ -843,6 +874,25 @@ Specifies if interval timer is reset when payload change was detected. + The name of the application that is responsible for the routing. +* `routing-credentials` ++ +The UID / GID of the application acting as routing manager. +(Must be specified if credentials checks are enabled using _check_credentials_ set to _true_ in order to successfully check the routing managers credentials passed on connect) + +** `uid` ++ +The routing managers UID. + +** `gid` ++ +The routing managers GID. + +* `shutdown_timeout` ++ +Configures the time in milliseconds local clients wait for acknowledgement of +their deregistration from the routing manager during shutdown. Defaults to +5000ms. + * `service-discovery` + Contains settings related to the Service Discovery of the host application. @@ -949,40 +999,40 @@ repetition phase. This can be used to reduce the number of sent messages during startup. The default setting is _500ms_. + //Watchdog -* anchor:config-watchdog[]'watchdog' (optional) +* anchor:config-watchdog[]`watchdog` (optional) + The Watchdog sends periodically pings to all known local clients. If a client isn't responding within a configurred time/amount of pongs the watchdog deregisters this application/client. If not configured the watchdog isn't activated. + -** 'enable' +** `enable` + Specifies whether the watchdog is enabled or disabled. (valid values: _true, false_), (default is _false_). + -** 'timeout' +** `timeout` + Specifies the timeout in ms the watchdog gets activated if a ping isn't answered with a pong by a local client within that time. (valid values: _2 - 2^32_), (default is _5000_ ms). + -** 'allowed_missing_pongs' +** `allowed_missing_pongs` + Specifies the amount of allowed missing pongs. (valid values: _1 - 2^32_), (default is _3_ pongs). + //CAPI-Selective Broadcasts support -* anchor:config-supports_selective_broadcasts[]'supports_selective_broadcasts' (optional) +* anchor:config-supports_selective_broadcasts[]`supports_selective_broadcasts` (optional) + This nodes allow to add a list of IP addresses on which CAPI-Selective-Broadcasts feature is supported. If not specified the feature can't be used and the subscription behavior of the stack is same as with normal events. + -** 'address' +** `address` + Specifies an IP-Address (in IPv4 or IPv6 notation) on which the "selective"-feature is supported. -Multiple addresses can be configuered. +Multiple addresses can be configured. Security -------- @@ -1010,20 +1060,27 @@ Security configuration The available configuration switches for the security feature are: // Security -* anchor:config-policy[]'security' (optional) +* anchor:config-policy[]`security` (optional) + If specified the credential passing mechanism is activated. However no credential or security checks are done as long as _check_credentials_ isn't set to _true_, but the routing manager client ID must be configured if security tag is specified and shall not be set to 0x6300. +If _check_credentials_ is set to _true_, the routing managers UID and GID needs to be specified using _routing-credentials_ tag. -** 'check_credentials (optional)' +** `check_credentials` (optional) + Specifies whether security checks are active or not. This includes credentials checks on connect as well as all policies checks configured in follow. (valid values: _true, false_), (default is _false_). -** 'policies' (array) +** `allow_remote_clients` (optional) ++ +Specifies whether incoming remote requests / subscriptions are allowed to be sent to a local proxy / client. +If not specified, all remote requests / subscriptions are allowed to be received by default. +(valid values are 'true' and 'false') + +** `policies` (array) + Specifies the security policies. Each policy at least needs to specify _allow_ or _deny_. -*** 'client' (optional) +*** `client` (optional) + Specifies a client for which a security policy will be applied (valid value: A valid client identifier in hex: e.g. _0x1234_). It is also possible to specify a client identifier range to easily apply a policy to a set of clients. @@ -1031,76 +1088,123 @@ A usecase is e.g. to allow a set of remote clients communicate with local servic + No client specification equals to any client (_0xFFFF_). Such policies are applied if a client has no specific policy. -**** 'first' +**** `first` + Specifies the first client of a range (first is included). (valid value: A valid client identifier in hex: e.g. _0x1234_) -**** 'last' +**** `last` + Specifies the last client id of a range (last is included). (valid value: A valid client identifier in hex: e.g. _0x1234_) -*** 'credentials' (optional) +*** `credential` (optional) + Specifies the credentials of the above client(s). If _check_credentials_ is set to _true_ the credentials for the above client(s) (if they running locally) needs to be specified correctly to ensure local socket authentification can succeed. This entry is optional due to the fact that remote clients needs to be configured as well to allow to communicate with local services as already mentioned above. For remote clients this entry should be skipped. -**** 'uid' +**** `uid` + Specifies the LINUX user id of the above client(s) as decimal number. As a wildcard "any" can be used. -**** 'gid' +**** `gid` + Specifies the LINUX group id of the above client(s) as decimal number. As a wildcard "any" can be used. -**** 'allow/deny' (optional) +**** `allow` / `deny` (optional) + Specifies whether the LINUX user and group ids are allowed or denied for the policy. -***** 'uid' (array) +. `uid` (array) + Specifies a list of LINUX user ids. These may either be specified as decimal numbers or as ranges. Ranges are specified by the first and the last valid id (see example below). -***** 'gid' (array) +. `gid` (array) + Specifies a list of LINUX group ids. These may either be specified as decimal numbers or as ranges. Ranges are specified by the first and the last valid id (see example below). -*** 'allow/deny' +*** `allow` / `deny` + This tag specifies either _allow_ or _deny_ depending on white- or blacklisting is needed. Specifing _allow_ and _deny_ entries in one policy is therefore not allowed. With _allow_ a whitelisting of what is allowed can be done which means an empty _allow_ tag implies everything is denied. With _deny_ a blacklisting of what is allowed can be done which means an empty _deny_ tag implies everything is allowed. -**** 'requests' (array) +**** `requests` (array) + -Specifies a set of serivce instance pairs which the above client(s) is allowed/denied to communicate with. +Specifies a set of service instance pairs which the above client(s) is allowed/denied to communicate with. -***** 'service' +. `service` + Specifies a service for the _requests_. -***** 'instance' +. `instance` (deprecated) + Specifies a instance for the _requests_ +As a wildcard "any" can be used which means a range from instance ID 0x01 to 0xFFFF +which also implies a method ID range from 0x01 to 0xFFFF. + +. `instances` (array) ++ +Specifies a set of instance ID and method ID range pairs which are allowed/denied to communicate with. +If the `ids` tag below is not used to specify allowed/denied requests on method ID level one can also +only specify a a set of instance ID ranges which are allowed/denied to be requested analogous to the +allowed/denied `offers` section. +If no method IDs are specified, the allowed/denied methods are by default a range from 0x01 to 0xFFFF. + +.. `ids` ++ +Specifies a set of instance ID ranges which are allowed/denied to communicate with. +It is also possible to specify a single instance ID as array element without giving an upper / lower range bound. +As a wildcard "any" can be used which means a range from instance ID 0x01 to 0xFFFF. ++ +`first` - The lower bound of the instance range. ++ +`last` - The upper bound of the instance range. -**** 'offers' (array) +.. `methods` ++ +Specifies a set of method ID ranges which are allowed/denied to communicate with. +It is also possible to specify a single method ID as array element without giving an upper / lower range bound. +As a wildcard "any" can be used which means a range from method ID 0x01 to 0xFFFF. ++ +`first` - The lower bound of the method range. ++ +`last` - The upper bound of the method range. + +**** `offers` (array) + Specifies a set of service instance pairs which are allowed/denied to be offered by the above client(s). -***** 'service' +. `service` + Specifies a service for the _offers_. -***** 'instance' +. `instance` (deprecated) + Specifies a instance for the _offers_ +As a wildcard "any" can be used which means a range from instance ID 0x01 to 0xFFFF. + +. `instances` (array) ++ +Specifies a set of instance ID ranges which are allowed/denied to be offered by the above client(s) +It is also possible to specify a single instance ID as array element without giving an upper / lower range bound. +As a wildcard "any" can be used which means a range from instance ID 0x01 to 0xFFFF. + +.. `first` ++ +The lower bound of the instance range. + +.. `last` ++ +The upper bound of the instance range. + +<<< Security configuration example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1140,18 +1244,51 @@ Security configuration example "gid" : [ "0", { "first" : "100", "last" : "243" }, "300"] }, { - "uid" : "55", - "gid" : "55" + "uid" : ["55"], + "gid" : ["55"] } ] }, "allow" : [ + "offers" : + [ + { + "service" : "0x6728", + "instances" : [ "0x0001", { "first" : "0x0003", "last" : "0x0007" }, "0x0009"] + }, + { + "service" : "0x6729", + "instances" : ["0x88"] + }, + { + "service" : "0x6730", + "instance" : "any" + } + ], "requests" : [ { "service" : "0x6732", - "instance" : "0x0001" + "instances" : + [ + { + "ids" : [ "0x0001", { "first" : "0x0003", "last" : "0x0007" }], + "methods" : [ "0x0001", "0x0003", { "first" : "0x8001", "last" : "0x8006" } ] + }, + { + "ids" : [ "0x0009" ], + "methods" : "any" + } + ] + }, + { + "service" : "0x6733", + "instance" : "0x1" + }, + { + "service" : "0x6733", + "instances" : [ "0x0002", { "first" : "0x0003", "last" : "0x0007" }, "0x0009"] } ] ] @@ -1496,7 +1633,7 @@ Overview/Prerequisites The Trace Connector is used to forward the internal messages that are sent over the Unix Domain Sockets to DLT. + -So a prerequisite is that DLT is installed and the module can be found in +Thus, it requires that DLT is installed and the DLT module can be found in the context of CMake. Configuration @@ -1550,9 +1687,7 @@ Example 2 (Using Filters) "filters" : [ { "channel" : "MC", - "services" : [ "0x1234" ], - "methods" : [ "0x80e8" ], - "clients" : [ "0x1343" ], + "matches" : [ { "service" : "0x1234", "instance" : "any", "method" : "0x80e8" } ], "type" : "positive" } ] @@ -1561,32 +1696,29 @@ Example 2 (Using Filters) ... ---- -You can apply filters to the messages. In this example only the messages that +As it is a positive filter, the example filter ensures that only messages +representing method '0x80e8' from instances of service '0x1234' will be +forwarded to the DLT. If it was specified as a negative filter, all messages +except messages representing method '0x80e8' from instances of service +'0x1234' would be forwarded to the DLT. -* are addressed to the service with the id _0x1234_ -+ -* relates to the method with the id _0x80e8_ -+ -* relates to the client with the id _0x1234_ +The general filter rules are: - -will be traced/forwarded to DLT. This is a kind of positive filtering. To -exclude messages from tracing/forwarding to DLT, the filter type can be -set to "negative". This would suppress the messages that relate to the -specified service, method and client ids. The default filter type is -psoitive. + -The messages will be forwarded over the channel with the id _MC_. If -just one filter is used, then the definition of a channel is optional. -But if multiple filters are used, each filter needs an own channel! + -In this example each criteria has only one expression/value but it's also possible -to define multiple values to get a more fine-grained filter. + -The ids of the filter criterias can be found in the appropriate _.fdepl_ files -and in the rest of the configuration file. + +* The default filter is a positive filter for all messages. +* The default filter is active on a channel as long as no other positive +filter is specified. +* Negative filters block matching messages. Negative filters overrule +positive filters. Thus, as soon as a messages matches a negative filter it +will not be forwarded. +* The identifier '0xffff' is a wildcard that matches any service, instance or method. +The keyword 'any' can be used as a replacement for '0xffff'. +* Wildcards must not be used within range filters. Dynamic Configuration ^^^^^^^^^^^^^^^^^^^^^ The Trace Connector can also be configured dynamically over its interfaces. +You need to include '<vsomeip/trace.hpp>' to access its public interface. [float] Example: @@ -1595,26 +1727,27 @@ Example: [source, bash] ---- // get trace connector - std::shared_ptr<tc::trace_connector> its_trace_connector = tc::trace_connector::get(); + std::shared_ptr<vsomeip::trace::connector> its_connector + = vsomeip::trace::connector::get(); // add channel - its_trace_connector->add_channel("MC", "My channel"); - - //add filter rule - tc::trace_connector::filter_rule_t its_filter_rule; + std::shared_ptr<vsomeip::trace::channel> its_channel + = its_connector->create_channel("MC", "My channel"); - its_filter_rule[tc::filter_criteria_e::SERVICES] = { 4660 }; - - its_filter_rule[tc::filter_criteria_e::METHODS] = { 33000 }; - its_filter_rule[tc::filter_criteria_e::CLIENTS] = { 4931 }; - - its_trace_connector->add_filter_rule("MC", its_filter_rule); + // add filter rule + vsomeip::trace::match_t its_match + = std::make_tuple(0x1234, 0xffff, 0x80e8); + vsomeip::trace::filter_id_t its_filter_id + = its_channel->add_filter(its_match, true); // init trace connector - its_trace_connector->init(); + its_connector->init(); // enable trace connector - its_trace_connector->set_enabled(true); + its_connector->set_enabled(true); + + // remove the filter + its_channel->remove_filter(its_filter_id); ---- Tools diff --git a/exportmap.gcc b/exportmap.gcc index 215ef98..aec5019 100644 --- a/exportmap.gcc +++ b/exportmap.gcc @@ -40,6 +40,7 @@ global: vsomeip::sd::runtime::*; *vsomeip::utility; vsomeip::utility::is*; + vsomeip::utility::parse*; *vsomeip::plugin_manager; vsomeip::plugin_manager::*; }; diff --git a/implementation/configuration/include/configuration.hpp b/implementation/configuration/include/configuration.hpp index c89ed39..9740a79 100644 --- a/implementation/configuration/include/configuration.hpp +++ b/implementation/configuration/include/configuration.hpp @@ -24,6 +24,7 @@ #include "../../e2e_protection/include/e2exf/config.hpp" #include "e2e.hpp" +#include "policy.hpp" #include "debounce.hpp" @@ -38,10 +39,22 @@ public: virtual ~configuration() {} virtual bool load(const std::string &_name) = 0; + virtual bool remote_offer_info_add(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled) = 0; + virtual bool remote_offer_info_remove(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled, + bool* _still_offered_remote) = 0; virtual const std::string &get_network() const = 0; virtual const boost::asio::ip::address & get_unicast_address() const = 0; + virtual const boost::asio::ip::address& get_netmask() const = 0; virtual unsigned short get_diagnosis_address() const = 0; virtual std::uint16_t get_diagnosis_mask() const = 0; virtual bool is_v4() const = 0; @@ -84,6 +97,7 @@ public: virtual std::size_t get_max_dispatchers(const std::string &_name) const = 0; virtual std::size_t get_max_dispatch_time(const std::string &_name) const = 0; virtual std::size_t get_io_thread_count(const std::string &_name) const = 0; + virtual int get_io_thread_nice_level(const std::string &_name) const = 0; virtual std::size_t get_request_debouncing(const std::string &_name) const = 0; virtual std::uint32_t get_max_message_size_local() const = 0; @@ -124,7 +138,7 @@ public: virtual uint32_t get_allowed_missing_pongs() const = 0; // File permissions - virtual std::uint32_t get_umask() const = 0; + virtual std::uint32_t get_permissions_uds() const = 0; virtual std::uint32_t get_permissions_shm() const = 0; virtual bool log_version() const = 0; @@ -133,11 +147,19 @@ public: // Security virtual bool is_security_enabled() const = 0; virtual bool is_client_allowed(client_t _client, service_t _service, - instance_t _instance) const = 0; + instance_t _instance, method_t _method, bool _is_request_service = false) const = 0; virtual bool is_offer_allowed(client_t _client, service_t _service, instance_t _instance) const = 0; virtual bool check_credentials(client_t _client, uint32_t _uid, uint32_t _gid) = 0; + virtual bool store_client_to_uid_gid_mapping(client_t _client, uint32_t _uid, uint32_t _gid) = 0; + virtual void store_uid_gid_to_client_mapping(uint32_t _uid, uint32_t _gid, client_t _client) = 0; + + virtual bool get_client_to_uid_gid_mapping(client_t _client, + std::pair<uint32_t, uint32_t> &_uid_gid) = 0; + virtual bool remove_client_to_uid_gid_mapping(client_t _client) = 0; + virtual bool get_uid_gid_to_client_mapping(std::pair<uint32_t, uint32_t> _uid_gid, + std::set<client_t> &_clients) = 0; // Plugins virtual std::map<plugin_type_e, std::set<std::string>> get_plugins( @@ -146,7 +168,7 @@ public: virtual void set_configuration_path(const std::string &_path) = 0; //E2E - virtual std::map<e2exf::data_identifier, std::shared_ptr<cfg::e2e>> get_e2e_configuration() const = 0; + virtual std::map<e2exf::data_identifier_t, std::shared_ptr<cfg::e2e>> get_e2e_configuration() const = 0; virtual bool is_e2e_enabled() const = 0; virtual bool log_memory() const = 0; @@ -170,6 +192,39 @@ public: virtual endpoint_queue_limit_t get_endpoint_queue_limit( const std::string& _address, std::uint16_t _port) const = 0; virtual endpoint_queue_limit_t get_endpoint_queue_limit_local() const = 0; + + virtual std::uint32_t get_max_tcp_restart_aborts() const = 0; + virtual std::uint32_t get_max_tcp_connect_time() const = 0; + + // Offer acceptance + virtual bool offer_acceptance_required( + const boost::asio::ip::address& _address) const = 0; + virtual void set_offer_acceptance_required( + const boost::asio::ip::address& _address, const std::string& _path, + bool _enable) = 0; + virtual std::map<boost::asio::ip::address, std::string> get_offer_acceptance_required() = 0; + + // Security policy + virtual void update_security_policy(uint32_t _uid, uint32_t _gid, ::std::shared_ptr<policy> _policy) = 0; + virtual bool remove_security_policy(uint32_t _uid, uint32_t _gid) = 0; + + virtual void add_security_credentials(uint32_t _uid, uint32_t _gid, + ::std::shared_ptr<policy> _credentials_policy, client_t _client) = 0; + + virtual bool is_remote_client_allowed() const = 0; + + virtual bool is_policy_update_allowed(uint32_t _uid, std::shared_ptr<policy> &_policy) const = 0; + + virtual bool is_policy_removal_allowed(uint32_t _uid) const = 0; + + virtual std::uint32_t get_udp_receive_buffer_size() const = 0; + + virtual bool is_audit_mode_enabled() const = 0; + + virtual bool check_routing_credentials(client_t _client, uint32_t _uid, uint32_t _gid) const = 0; + + // routing shutdown timeout + virtual std::uint32_t get_shutdown_timeout() const = 0; }; } // namespace vsomeip diff --git a/implementation/configuration/include/configuration_impl.hpp b/implementation/configuration/include/configuration_impl.hpp index af04ce1..9bf1188 100644 --- a/implementation/configuration/include/configuration_impl.hpp +++ b/implementation/configuration/include/configuration_impl.hpp @@ -49,16 +49,28 @@ class configuration_impl: public std::enable_shared_from_this<configuration_impl> { public: VSOMEIP_EXPORT configuration_impl(); - VSOMEIP_EXPORT configuration_impl(const configuration_impl &_cfg); + VSOMEIP_EXPORT configuration_impl(const configuration_impl &_other); VSOMEIP_EXPORT virtual ~configuration_impl(); VSOMEIP_EXPORT bool load(const std::string &_name); + VSOMEIP_EXPORT bool remote_offer_info_add(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled); + VSOMEIP_EXPORT bool remote_offer_info_remove(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled, + bool* _still_offered_remote); VSOMEIP_EXPORT const std::string &get_network() const; VSOMEIP_EXPORT void set_configuration_path(const std::string &_path); VSOMEIP_EXPORT const boost::asio::ip::address & get_unicast_address() const; + VSOMEIP_EXPORT const boost::asio::ip::address& get_netmask() const; VSOMEIP_EXPORT unsigned short get_diagnosis_address() const; VSOMEIP_EXPORT std::uint16_t get_diagnosis_mask() const; VSOMEIP_EXPORT bool is_v4() const; @@ -91,6 +103,7 @@ public: VSOMEIP_EXPORT std::size_t get_max_dispatchers(const std::string &_name) const; VSOMEIP_EXPORT std::size_t get_max_dispatch_time(const std::string &_name) const; VSOMEIP_EXPORT std::size_t get_io_thread_count(const std::string &_name) const; + VSOMEIP_EXPORT int get_io_thread_nice_level(const std::string &_name) const; VSOMEIP_EXPORT std::size_t get_request_debouncing(const std::string &_name) const; VSOMEIP_EXPORT std::set<std::pair<service_t, instance_t> > get_remote_services() const; @@ -140,22 +153,30 @@ public: VSOMEIP_EXPORT uint32_t get_watchdog_timeout() const; VSOMEIP_EXPORT uint32_t get_allowed_missing_pongs() const; - VSOMEIP_EXPORT std::uint32_t get_umask() const; + VSOMEIP_EXPORT std::uint32_t get_permissions_uds() const; VSOMEIP_EXPORT std::uint32_t get_permissions_shm() const; // Policy VSOMEIP_EXPORT bool is_security_enabled() const; VSOMEIP_EXPORT bool is_client_allowed(client_t _client, service_t _service, - instance_t _instance) const; + instance_t _instance, method_t _method, bool _is_request_service = false) const; VSOMEIP_EXPORT bool is_offer_allowed(client_t _client, service_t _service, instance_t _instance) const; VSOMEIP_EXPORT bool check_credentials(client_t _client, uint32_t _uid, uint32_t _gid); + VSOMEIP_EXPORT bool store_client_to_uid_gid_mapping(client_t _client, uint32_t _uid, uint32_t _gid); + VSOMEIP_EXPORT void store_uid_gid_to_client_mapping(uint32_t _uid, uint32_t _gid, client_t _client); + VSOMEIP_EXPORT bool get_client_to_uid_gid_mapping(client_t _client, + std::pair<uint32_t, uint32_t> &_uid_gid); + VSOMEIP_EXPORT bool remove_client_to_uid_gid_mapping(client_t _client); + VSOMEIP_EXPORT bool get_uid_gid_to_client_mapping(std::pair<uint32_t, uint32_t> _uid_gid, + std::set<client_t> &_clients); + VSOMEIP_EXPORT std::map<plugin_type_e, std::set<std::string>> get_plugins( const std::string &_name) const; // E2E - VSOMEIP_EXPORT std::map<e2exf::data_identifier, std::shared_ptr<cfg::e2e>> get_e2e_configuration() const; + VSOMEIP_EXPORT std::map<e2exf::data_identifier_t, std::shared_ptr<cfg::e2e>> get_e2e_configuration() const; VSOMEIP_EXPORT bool is_e2e_enabled() const; VSOMEIP_EXPORT bool log_memory() const; @@ -173,11 +194,42 @@ public: VSOMEIP_EXPORT endpoint_queue_limit_t get_endpoint_queue_limit( const std::string& _address, std::uint16_t _port) const; VSOMEIP_EXPORT endpoint_queue_limit_t get_endpoint_queue_limit_local() const; + + VSOMEIP_EXPORT std::uint32_t get_max_tcp_restart_aborts() const; + VSOMEIP_EXPORT std::uint32_t get_max_tcp_connect_time() const; + + VSOMEIP_EXPORT bool offer_acceptance_required( + const boost::asio::ip::address& _address) const; + + VSOMEIP_EXPORT void set_offer_acceptance_required( + const boost::asio::ip::address& _address, const std::string& _path, + bool _enable); + VSOMEIP_EXPORT std::map<boost::asio::ip::address, std::string> get_offer_acceptance_required(); + + VSOMEIP_EXPORT void update_security_policy(uint32_t _uid, uint32_t _gid, ::std::shared_ptr<policy> _policy); + VSOMEIP_EXPORT bool remove_security_policy(uint32_t _uid, uint32_t _gid); + + VSOMEIP_EXPORT void add_security_credentials(uint32_t _uid, uint32_t _gid, + ::std::shared_ptr<policy> _credentials_policy, client_t _client); + + VSOMEIP_EXPORT bool is_remote_client_allowed() const; + + VSOMEIP_EXPORT bool is_policy_update_allowed(uint32_t _uid, std::shared_ptr<policy> &_policy) const; + + VSOMEIP_EXPORT bool is_policy_removal_allowed(uint32_t _uid) const; + + VSOMEIP_EXPORT std::uint32_t get_udp_receive_buffer_size() const; + + VSOMEIP_EXPORT bool is_audit_mode_enabled() const; + + VSOMEIP_EXPORT bool check_routing_credentials(client_t _client, uint32_t _uid, uint32_t _gid) const; + + VSOMEIP_EXPORT std::uint32_t get_shutdown_timeout() const; private: void read_data(const std::set<std::string> &_input, std::vector<element> &_elements, std::set<std::string> &_failed, - bool _standard_only); + bool _mandatory_only); bool load_data(const std::vector<element> &_elements, bool _load_mandatory, bool _load_optional); @@ -185,10 +237,11 @@ private: bool load_logging(const element &_element, std::set<std::string> &_warnings); bool load_routing(const element &_element); + bool load_routing_credentials(const element &_element); bool load_applications(const element &_element); void load_application_data(const boost::property_tree::ptree &_tree, - const std::string &_name); + const std::string &_file_name); void load_tracing(const element &_element); void load_trace_channels(const boost::property_tree::ptree &_tree); @@ -198,12 +251,17 @@ private: void load_trace_filter_expressions( const boost::property_tree::ptree &_tree, std::string &_criteria, - std::shared_ptr<trace_filter_rule> &_filter_rule); + std::shared_ptr<trace_filter> &_filter); + void load_trace_filter_match( + const boost::property_tree::ptree &_data, + std::tuple<service_t, instance_t, method_t> &_match); void load_network(const element &_element); void load_unicast_address(const element &_element); + void load_netmask(const element &_element); void load_diagnosis_address(const element &_element); + void load_shutdown_timeout(const element &_element); void load_service_discovery(const element &_element); void load_delays(const boost::property_tree::ptree &_tree); @@ -234,6 +292,11 @@ private: void load_policy(const boost::property_tree::ptree &_tree); void load_credential(const boost::property_tree::ptree &_tree, ids_t &_ids); void load_ranges(const boost::property_tree::ptree &_tree, ranges_t &_range); + void load_instance_ranges(const boost::property_tree::ptree &_tree, ranges_t &_range); + + void load_security_update_whitelist(const element &_element); + void load_service_ranges(const boost::property_tree::ptree &_tree, + std::set<std::pair<service_t, service_t>> &_ranges); void load_debounce(const element &_element); void load_service_debounce(const boost::property_tree::ptree &_tree); @@ -243,11 +306,15 @@ private: std::map<event_t, std::shared_ptr<debounce>> &_debounces); void load_event_debounce_ignore(const boost::property_tree::ptree &_tree, std::map<std::size_t, byte_t> &_ignore); + void load_offer_acceptance_required(const element &_element); + void load_udp_receive_buffer_size(const element &_element); + servicegroup *find_servicegroup(const std::string &_name) const; std::shared_ptr<client> find_client(service_t _service, instance_t _instance) const; std::shared_ptr<service> find_service(service_t _service, instance_t _instance) const; + std::shared_ptr<service> find_service_unlocked(service_t _service, instance_t _instance) const; std::shared_ptr<eventgroup> find_eventgroup(service_t _service, instance_t _instance, eventgroup_t _eventgroup) const; bool find_port(uint16_t &_port, uint16_t _remote, bool _reliable, @@ -271,8 +338,14 @@ private: void load_endpoint_queue_sizes(const element &_element); + void load_tcp_restart_settings(const element &_element); + + std::shared_ptr<policy> find_client_id_policy(client_t _client) const; + private: std::mutex mutex_; + mutable std::mutex ids_mutex_; + mutable std::mutex uid_to_clients_mutex_; bool is_loaded_; bool is_logging_loaded_; @@ -282,6 +355,7 @@ private: protected: // Configuration data boost::asio::ip::address unicast_; + boost::asio::ip::address netmask_; unsigned short diagnosis_; std::uint16_t diagnosis_mask_; @@ -292,9 +366,10 @@ protected: boost::log::trivial::severity_level loglevel_; std::map<std::string, std::tuple<client_t, std::size_t, std::size_t, - size_t, size_t, std::map<plugin_type_e, std::set<std::string>>>> applications_; + size_t, size_t, std::map<plugin_type_e, std::set<std::string>>, int>> applications_; std::set<client_t> client_identifiers_; + mutable std::mutex services_mutex_; std::map<service_t, std::map<instance_t, std::shared_ptr<service> > > services_; @@ -368,24 +443,39 @@ protected: ET_ENDPOINT_QUEUE_LIMITS, ET_ENDPOINT_QUEUE_LIMIT_EXTERNAL, ET_ENDPOINT_QUEUE_LIMIT_LOCAL, - ET_MAX = 31 + ET_TCP_RESTART_ABORTS_MAX, + ET_TCP_CONNECT_TIME_MAX, + ET_OFFER_ACCEPTANCE_REQUIRED, + ET_NETMASK, + ET_UDP_RECEIVE_BUFFER_SIZE, + ET_ROUTING_CREDENTIALS, + ET_SHUTDOWN_TIMEOUT, + ET_MAX = 38 }; bool is_configured_[ET_MAX]; std::uint32_t permissions_shm_; - std::uint32_t umask_; + std::uint32_t permissions_uds_; - std::map<client_t, std::shared_ptr<policy>> policies_; + std::map<std::pair<uint16_t, uint16_t>, std::shared_ptr<policy>> policies_; std::vector<std::shared_ptr<policy> > any_client_policies_; + + mutable std::mutex policies_mutex_; + mutable std::mutex any_client_policies_mutex_; std::map<client_t, std::pair<uint32_t, uint32_t> > ids_; + std::map<std::pair<uint32_t, uint32_t>, std::set<client_t> > uid_to_clients_; + bool policy_enabled_; bool check_credentials_; + bool check_routing_credentials_; + bool allow_remote_clients_; + bool check_whitelist_; std::string network_; std::string configuration_path_; bool e2e_enabled_; - std::map<e2exf::data_identifier, std::shared_ptr<cfg::e2e>> e2e_configuration_; + std::map<e2exf::data_identifier_t, std::shared_ptr<cfg::e2e>> e2e_configuration_; bool log_memory_; uint32_t log_memory_interval_; @@ -401,6 +491,28 @@ protected: std::map<std::string, std::map<std::uint16_t, endpoint_queue_limit_t>> endpoint_queue_limits_; endpoint_queue_limit_t endpoint_queue_limit_external_; endpoint_queue_limit_t endpoint_queue_limit_local_; + + uint32_t tcp_restart_aborts_max_; + uint32_t tcp_connect_time_max_; + + mutable std::mutex offer_acceptance_required_ips_mutex_; + std::map<boost::asio::ip::address, std::string> offer_acceptance_required_ips_; + + bool has_issued_methods_warning_; + bool has_issued_clients_warning_; + + std::uint32_t udp_receive_buffer_size_; + + mutable std::mutex service_interface_whitelist_mutex_; + std::set<std::pair<service_t, service_t>> service_interface_whitelist_; + + mutable std::mutex uid_whitelist_mutex_; + ranges_t uid_whitelist_; + + mutable std::mutex routing_credentials_mutex_; + std::pair<uint32_t, uint32_t> routing_credentials_; + + std::uint32_t shutdown_timeout_; }; } // namespace cfg diff --git a/implementation/configuration/include/e2e.hpp b/implementation/configuration/include/e2e.hpp index 6f7bcda..94eb33d 100644 --- a/implementation/configuration/include/e2e.hpp +++ b/implementation/configuration/include/e2e.hpp @@ -30,8 +30,8 @@ struct e2e { } - e2e(uint16_t _data_id, std::string _variant, std::string _profile, uint16_t _service_id, - uint16_t _event_id,uint16_t _crc_offset, + e2e(uint16_t _data_id, std::string _variant, std::string _profile, service_t _service_id, + event_t _event_id,uint16_t _crc_offset, uint8_t _data_id_mode, uint16_t _data_length, uint16_t _data_id_nibble_offset, uint16_t _counter_offset) : data_id(_data_id), @@ -51,8 +51,8 @@ struct e2e { uint16_t data_id; std::string variant; std::string profile; - uint16_t service_id; - uint16_t event_id; + service_t service_id; + event_t event_id; //profile 1 specific config // [SWS_E2E_00018] diff --git a/implementation/configuration/include/internal.hpp.in b/implementation/configuration/include/internal.hpp.in index 1cb9895..b1a1372 100644 --- a/implementation/configuration/include/internal.hpp.in +++ b/implementation/configuration/include/internal.hpp.in @@ -16,11 +16,10 @@ #define VSOMEIP_ENV_MANDATORY_CONFIGURATION_FILES "VSOMEIP_MANDATORY_CONFIGURATION_FILES" #define VSOMEIP_ENV_LOAD_PLUGINS "VSOMEIP_LOAD_PLUGINS" #define VSOMEIP_ENV_CLIENTSIDELOGGING "VSOMEIP_CLIENTSIDELOGGING" -#define VSOMEIP_ENV_DEBUG_CONFIGURATION "VSOMEIP_DEBUG_CONFIGURATION" #define VSOMEIP_DEFAULT_CONFIGURATION_FILE "/etc/vsomeip.json" #define VSOMEIP_LOCAL_CONFIGURATION_FILE "./vsomeip.json" -#define VSOMEIP_MANDATORY_CONFIGURATION_FILES "vsomeip_std.json,vsomeip_app.json,vsomeip_plc.json,vsomeip_log.json,vsomeip_security.json" +#define VSOMEIP_MANDATORY_CONFIGURATION_FILES "vsomeip_std.json,vsomeip_app.json,vsomeip_plc.json,vsomeip_log.json,vsomeip_security.json,vsomeip_whitelist.json" #define VSOMEIP_DEFAULT_CONFIGURATION_FOLDER "/etc/vsomeip" #define VSOMEIP_DEBUG_CONFIGURATION_FOLDER "/var/opt/public/sin/vsomeip/" @@ -50,15 +49,26 @@ #endif #define VSOMEIP_UNICAST_ADDRESS "@VSOMEIP_UNICAST_ADDRESS@" +#define VSOMEIP_NETMASK "255.255.255.0" #define VSOMEIP_DEFAULT_CONNECT_TIMEOUT 100 #define VSOMEIP_MAX_CONNECT_TIMEOUT 1600 #define VSOMEIP_DEFAULT_FLUSH_TIMEOUT 1000 +#define VSOMEIP_DEFAULT_SHUTDOWN_TIMEOUT 5000 + +#define VSOMEIP_MAX_TCP_CONNECT_TIME 5000 +#define VSOMEIP_MAX_TCP_RESTART_ABORTS 5 + +#define VSOMEIP_DEFAULT_BUFFER_SHRINK_THRESHOLD 5 + #define VSOMEIP_DEFAULT_WATCHDOG_TIMEOUT 5000 #define VSOMEIP_DEFAULT_MAX_MISSING_PONGS 3 +#define VSOMEIP_DEFAULT_UDP_RCV_BUFFER_SIZE 1703936 + #define VSOMEIP_IO_THREAD_COUNT 2 +#define VSOMEIP_IO_THREAD_NICE_LEVEL 255 #define VSOMEIP_MAX_DISPATCHERS 10 #define VSOMEIP_MAX_DISPATCH_TIME 100 @@ -104,6 +114,14 @@ #define VSOMEIP_OFFERED_SERVICES_REQUEST 0x1F #define VSOMEIP_OFFERED_SERVICES_RESPONSE 0x20 #define VSOMEIP_UNSUBSCRIBE_ACK 0x21 +#define VSOMEIP_RESEND_PROVIDED_EVENTS 0x22 + +#define VSOMEIP_UPDATE_SECURITY_POLICY 0x23 +#define VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE 0x24 +#define VSOMEIP_REMOVE_SECURITY_POLICY 0x25 +#define VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE 0x26 +#define VSOMEIP_UPDATE_SECURITY_CREDENTIALS 0x27 +#define VSOMEIP_DISTRIBUTE_SECURITY_POLICIES 0x28 #define VSOMEIP_SEND_COMMAND_SIZE 14 #define VSOMEIP_SEND_COMMAND_INSTANCE_POS_MIN 7 @@ -129,6 +147,16 @@ #define VSOMEIP_ID_RESPONSE_COMMAND_SIZE 12 #define VSOMEIP_ID_REQUEST_COMMAND_SIZE 13 #define VSOMEIP_OFFERED_SERVICES_COMMAND_SIZE 8 +#define VSOMEIP_RESEND_PROVIDED_EVENTS_COMMAND_SIZE 11 +#define VSOMEIP_REMOVE_SECURITY_POLICY_COMMAND_SIZE 19 +#define VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE 11 +#define VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE 11 +#define VSOMEIP_PING_COMMAND_SIZE 7 +#define VSOMEIP_PONG_COMMAND_SIZE 7 +#define VSOMEIP_REGISTER_APPLICATION_COMMAND_SIZE 7 +#define VSOMEIP_DEREGISTER_APPLICATION_COMMAND_SIZE 7 +#define VSOMEIP_REGISTERED_ACK_COMMAND_SIZE 7 + #ifndef _WIN32 #include <pthread.h> @@ -138,7 +166,7 @@ #define VSOMEIP_DIAGNOSIS_ADDRESS @VSOMEIP_DIAGNOSIS_ADDRESS@ #define VSOMEIP_DEFAULT_SHM_PERMISSION 0666 -#define VSOMEIP_DEFAULT_UMASK_LOCAL_ENDPOINTS 0000 +#define VSOMEIP_DEFAULT_UDS_PERMISSIONS 0666 #define VSOMEIP_ROUTING_READY_MESSAGE "@VSOMEIP_ROUTING_READY_MESSAGE@" @@ -180,7 +208,7 @@ struct configuration_data_t { unsigned short client_base_; unsigned short max_clients_; int max_used_client_ids_index_; - unsigned short max_assigned_client_id_without_diagnosis_; + unsigned short max_assigned_client_id_; unsigned short routing_manager_host_; // array of used client ids here, pointer to it is kept in utility class }; @@ -189,6 +217,8 @@ const std::uint32_t MESSAGE_SIZE_UNLIMITED = (std::numeric_limits<std::uint32_t> const std::uint32_t QUEUE_SIZE_UNLIMITED = (std::numeric_limits<std::uint32_t>::max)(); +const std::uint32_t MAX_RECONNECTS_UNLIMITED = (std::numeric_limits<std::uint32_t>::max)(); + } // namespace vsomeip diff --git a/implementation/configuration/include/policy.hpp b/implementation/configuration/include/policy.hpp index 9132a4d..c8f649e 100644 --- a/implementation/configuration/include/policy.hpp +++ b/implementation/configuration/include/policy.hpp @@ -12,7 +12,6 @@ #include <vsomeip/primitive_types.hpp> namespace vsomeip { -namespace cfg { typedef std::set<std::pair<uint32_t, uint32_t>> ranges_t; typedef std::set<std::pair<ranges_t, ranges_t>> ids_t; @@ -23,12 +22,11 @@ struct policy { ids_t ids_; bool allow_who_; - std::set<std::pair<service_t, instance_t>> services_; - std::set<std::pair<service_t, instance_t>> offers_; + std::set<std::pair<service_t, ids_t>> services_; + std::set<std::pair<service_t, ranges_t>> offers_; bool allow_what_; }; -} // namespace cfg } // namespace vsomeip #endif // VSOMEIP_CFG_POLICY_HPP diff --git a/implementation/configuration/include/trace.hpp b/implementation/configuration/include/trace.hpp index db92286..3d4e1f4 100644 --- a/implementation/configuration/include/trace.hpp +++ b/implementation/configuration/include/trace.hpp @@ -3,67 +3,51 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#ifndef CONFIGURATION_INCLUDE_TRACE_HPP_ -#define CONFIGURATION_INCLUDE_TRACE_HPP_ +#ifndef CFG_TRACE_HPP_ +#define CFG_TRACE_HPP_ #include <string> #include <vector> #include <vsomeip/primitive_types.hpp> - -#include "../../tracing/include/defines.hpp" +#include <vsomeip/trace.hpp> namespace vsomeip { namespace cfg { struct trace_channel { - - trace_channel() : - id_(VSOMEIP_TC_DEFAULT_CHANNEL_ID), - name_(VSOMEIP_TC_DEFAULT_CHANNEL_NAME) { - - } - trace_channel_t id_; std::string name_; }; -struct trace_filter_rule { - - trace_filter_rule() : - channel_(VSOMEIP_TC_DEFAULT_CHANNEL_ID), - services_(), - methods_(), - clients_(), - type_(VSOMEIP_TC_DEFAULT_FILTER_TYPE) { - +struct trace_filter { + trace_filter() + : is_positive_(true), + is_range_(false) { } - trace_channel_t channel_; - std::vector<service_t> services_; - std::vector<method_t> methods_; - std::vector<client_t> clients_; - trace_filter_type_t type_; + std::vector<trace_channel_t> channels_; + bool is_positive_; + bool is_range_; + std::vector<vsomeip::trace::match_t> matches_; }; struct trace { - - trace() : - channels_(), - filter_rules_(), - is_enabled_(false), - is_sd_enabled_(false) { - channels_.push_back(std::make_shared<trace_channel>()); + trace() + : is_enabled_(false), + is_sd_enabled_(false), + channels_(), + filters_() { } - std::vector<std::shared_ptr<trace_channel>> channels_; - std::vector<std::shared_ptr<trace_filter_rule>> filter_rules_; - bool is_enabled_; bool is_sd_enabled_; + + std::vector<std::shared_ptr<trace_channel>> channels_; + std::vector<std::shared_ptr<trace_filter>> filters_; }; } // namespace cfg } // namespace vsomeip -#endif /* CONFIGURATION_INCLUDE_TRACE_HPP_ */ +#endif // CFG_TRACE_HPP_ diff --git a/implementation/configuration/src/configuration_impl.cpp b/implementation/configuration/src/configuration_impl.cpp index ccf3883..b574841 100644 --- a/implementation/configuration/src/configuration_impl.cpp +++ b/implementation/configuration/src/configuration_impl.cpp @@ -64,15 +64,18 @@ configuration_impl::configuration_impl() max_configured_message_size_(0), max_local_message_size_(0), max_reliable_message_size_(0), - buffer_shrink_threshold_(0), + buffer_shrink_threshold_(VSOMEIP_DEFAULT_BUFFER_SHRINK_THRESHOLD), trace_(std::make_shared<trace>()), watchdog_(std::make_shared<watchdog>()), log_version_(true), log_version_interval_(10), permissions_shm_(VSOMEIP_DEFAULT_SHM_PERMISSION), - umask_(VSOMEIP_DEFAULT_UMASK_LOCAL_ENDPOINTS), + permissions_uds_(VSOMEIP_DEFAULT_UDS_PERMISSIONS), policy_enabled_(false), check_credentials_(false), + check_routing_credentials_(false), + allow_remote_clients_(true), + check_whitelist_(false), network_("vsomeip"), e2e_enabled_(false), log_memory_(false), @@ -80,8 +83,15 @@ configuration_impl::configuration_impl() log_status_(false), log_status_interval_(0), endpoint_queue_limit_external_(QUEUE_SIZE_UNLIMITED), - endpoint_queue_limit_local_(QUEUE_SIZE_UNLIMITED) { + endpoint_queue_limit_local_(QUEUE_SIZE_UNLIMITED), + tcp_restart_aborts_max_(VSOMEIP_MAX_TCP_RESTART_ABORTS), + tcp_connect_time_max_(VSOMEIP_MAX_TCP_CONNECT_TIME), + has_issued_methods_warning_(false), + has_issued_clients_warning_(false), + udp_receive_buffer_size_(VSOMEIP_DEFAULT_UDP_RCV_BUFFER_SIZE), + shutdown_timeout_(VSOMEIP_DEFAULT_SHUTDOWN_TIMEOUT) { unicast_ = unicast_.from_string(VSOMEIP_UNICAST_ADDRESS); + netmask_ = netmask_.from_string(VSOMEIP_NETMASK); for (auto i = 0; i < ET_MAX; i++) is_configured_[i] = false; } @@ -97,14 +107,21 @@ configuration_impl::configuration_impl(const configuration_impl &_other) max_reliable_message_size_(_other.max_reliable_message_size_), buffer_shrink_threshold_(_other.buffer_shrink_threshold_), permissions_shm_(VSOMEIP_DEFAULT_SHM_PERMISSION), - umask_(VSOMEIP_DEFAULT_UMASK_LOCAL_ENDPOINTS), + permissions_uds_(VSOMEIP_DEFAULT_UDS_PERMISSIONS), endpoint_queue_limit_external_(_other.endpoint_queue_limit_external_), - endpoint_queue_limit_local_(_other.endpoint_queue_limit_local_) { + endpoint_queue_limit_local_(_other.endpoint_queue_limit_local_), + tcp_restart_aborts_max_(_other.tcp_restart_aborts_max_), + tcp_connect_time_max_(_other.tcp_connect_time_max_), + udp_receive_buffer_size_(_other.udp_receive_buffer_size_), + shutdown_timeout_(_other.shutdown_timeout_) { applications_.insert(_other.applications_.begin(), _other.applications_.end()); + client_identifiers_ = _other.client_identifiers_; services_.insert(_other.services_.begin(), _other.services_.end()); + clients_ = _other.clients_; unicast_ = _other.unicast_; + netmask_ = _other.netmask_; diagnosis_ = _other.diagnosis_; diagnosis_mask_ = _other.diagnosis_mask_; @@ -132,26 +149,48 @@ configuration_impl::configuration_impl(const configuration_impl &_other) sd_offer_debounce_time_ = _other.sd_offer_debounce_time_; trace_ = std::make_shared<trace>(*_other.trace_.get()); + supported_selective_addresses = _other.supported_selective_addresses; watchdog_ = std::make_shared<watchdog>(*_other.watchdog_.get()); + internal_service_ranges_ = _other.internal_service_ranges_; log_version_ = _other.log_version_; log_version_interval_ = _other.log_version_interval_; magic_cookies_.insert(_other.magic_cookies_.begin(), _other.magic_cookies_.end()); + message_sizes_ = _other.message_sizes_; for (auto i = 0; i < ET_MAX; i++) is_configured_[i] = _other.is_configured_[i]; + policies_ = _other.policies_; + any_client_policies_ = _other.any_client_policies_; + ids_ = _other.ids_; policy_enabled_ = _other.policy_enabled_; check_credentials_ = _other.check_credentials_; + check_routing_credentials_ = _other.check_routing_credentials_; + allow_remote_clients_ = _other.allow_remote_clients_; + check_whitelist_ = _other.check_whitelist_; + network_ = _other.network_; + configuration_path_ = _other.configuration_path_; + e2e_enabled_ = _other.e2e_enabled_; + e2e_configuration_ = _other.e2e_configuration_; log_memory_ = _other.log_memory_; log_memory_interval_ = _other.log_memory_interval_; log_status_ = _other.log_status_; log_status_interval_ = _other.log_status_interval_; + ttl_factors_offers_ = _other.ttl_factors_offers_; + ttl_factors_subscriptions_ = _other.ttl_factors_subscriptions_; + debounces_ = _other.debounces_; + endpoint_queue_limits_ = _other.endpoint_queue_limits_; + + offer_acceptance_required_ips_ = _other.offer_acceptance_required_ips_; + + has_issued_methods_warning_ = _other.has_issued_methods_warning_; + has_issued_clients_warning_ = _other.has_issued_clients_warning_; } configuration_impl::~configuration_impl() { @@ -195,14 +234,14 @@ bool configuration_impl::load(const std::string &_name) { } if (its_folder != "") { its_input.insert(its_folder); - } - - // Add debug configuration folder/file on top of already set input - const char* its_debug_env = getenv(VSOMEIP_ENV_DEBUG_CONFIGURATION); - if (nullptr != its_debug_env) { - its_input.insert(its_debug_env); - } else { - its_input.insert(VSOMEIP_DEBUG_CONFIGURATION_FOLDER); +#ifndef _WIN32 + // load security configuration files from UID_GID sub folder if existing + std::stringstream its_security_config_folder; + its_security_config_folder << its_folder << "/" << getuid() << "_" << getgid(); + if (utility::is_folder(its_security_config_folder.str())) { + its_input.insert(its_security_config_folder.str()); + } +#endif } // Determine standard configuration file @@ -271,6 +310,101 @@ bool configuration_impl::load(const std::string &_name) { return is_loaded_; } +bool configuration_impl::remote_offer_info_add(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled) { + bool ret = false; + if (!is_loaded_) { + VSOMEIP_ERROR << __func__ << " shall only be called after normal" + "configuration has been parsed"; + } else { + std::shared_ptr<service> its_service(std::make_shared<service>()); + its_service->service_ = _service; + its_service->instance_ = _instance; + its_service->reliable_ = its_service->unreliable_ = ILLEGAL_PORT; + _reliable ? + its_service->reliable_ = _port : + its_service->unreliable_ = _port; + its_service->unicast_address_ = "local"; + its_service->multicast_address_ = ""; + its_service->multicast_port_ = ILLEGAL_PORT; + its_service->protocol_ = "someip"; + + { + std::lock_guard<std::mutex> its_lock(services_mutex_); + bool updated(false); + auto found_service = services_.find(its_service->service_); + if (found_service != services_.end()) { + auto found_instance = found_service->second.find(its_service->instance_); + if (found_instance != found_service->second.end()) { + VSOMEIP_INFO << "Updating remote configuration for service [" + << std::hex << std::setw(4) << std::setfill('0') + << its_service->service_ << "." << its_service->instance_ << "]"; + if (_reliable) { + found_instance->second->reliable_ = its_service->reliable_; + } else { + found_instance->second->unreliable_ = its_service->unreliable_; + } + updated = true; + } + } + if (!updated) { + services_[_service][_instance] = its_service; + VSOMEIP_INFO << "Added new remote configuration for service [" + << std::hex << std::setw(4) << std::setfill('0') + << its_service->service_ << "." + << std::hex << std::setw(4) << std::setfill('0') + << its_service->instance_ << "]"; + } + if (_magic_cookies_enabled) { + magic_cookies_[its_service->unicast_address_].insert(its_service->reliable_); + } + } + ret = true; + } + return ret; +} + +bool configuration_impl::remote_offer_info_remove(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled, + bool* _still_offered_remote) { + (void)_port; + (void)_magic_cookies_enabled; + bool ret = false; + if (!is_loaded_) { + VSOMEIP_ERROR << __func__ << " shall only be called after normal" + "configuration has been parsed"; + } else { + std::lock_guard<std::mutex> its_lock(services_mutex_); + auto found_service = services_.find(_service); + if (found_service != services_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + VSOMEIP_INFO << "Removing remote configuration for service [" + << std::hex << std::setw(4) << std::setfill('0') + << _service << "." << _instance << "]"; + if (_reliable) { + found_instance->second->reliable_ = ILLEGAL_PORT; + // TODO delete from magic_cookies_map without overwriting + // configurations from other services offered on the same port + } else { + found_instance->second->unreliable_ = ILLEGAL_PORT; + } + *_still_offered_remote = ( + found_instance->second->unreliable_ != ILLEGAL_PORT || + found_instance->second->reliable_ != ILLEGAL_PORT); + ret = true; + } + } + } + return ret; +} + void configuration_impl::read_data(const std::set<std::string> &_input, std::vector<element> &_elements, std::set<std::string> &_failed, bool _mandatory_only) { @@ -339,17 +473,23 @@ bool configuration_impl::load_data(const std::vector<element> &_elements, has_applications = load_applications(e) || has_applications; load_network(e); load_diagnosis_address(e); + load_shutdown_timeout(e); load_payload_sizes(e); load_endpoint_queue_sizes(e); + load_tcp_restart_settings(e); load_permissions(e); load_policies(e); load_tracing(e); + load_udp_receive_buffer_size(e); + load_security_update_whitelist(e); + load_routing_credentials(e); } } if (_load_optional) { for (auto e : _elements) { load_unicast_address(e); + load_netmask(e); load_service_discovery(e); load_services(e); load_internal_services(e); @@ -358,6 +498,7 @@ bool configuration_impl::load_data(const std::vector<element> &_elements, load_selective_broadcasts_support(e); load_e2e(e); load_debounce(e); + load_offer_acceptance_required(e); } } @@ -476,6 +617,51 @@ bool configuration_impl::load_routing(const element &_element) { return true; } +bool configuration_impl::load_routing_credentials(const element &_element) { + try { + auto its_routing_cred = _element.tree_.get_child("routing-credentials"); + if (is_configured_[ET_ROUTING_CREDENTIALS]) { + VSOMEIP_WARNING << "vSomeIP Security: Multiple definitions of routing-credentials." + << " Ignoring definition from " << _element.name_; + } else { + for (auto i = its_routing_cred.begin(); + i != its_routing_cred.end(); + ++i) { + std::string its_key(i->first); + std::string its_value(i->second.data()); + std::stringstream its_converter; + if (its_key == "uid") { + uint32_t its_uid(0); + if (its_value.find("0x") == 0) { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + its_converter >> its_uid; + std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); + std::get<0>(routing_credentials_) = its_uid; + } else if (its_key == "gid") { + uint32_t its_gid(0); + if (its_value.find("0x") == 0) { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + its_converter >> its_gid; + std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); + std::get<1>(routing_credentials_) = its_gid; + } + } + check_routing_credentials_ = true; + is_configured_[ET_ROUTING_CREDENTIALS] = true; + } + } catch (...) { + return false; + } + return true; +} + + bool configuration_impl::load_applications(const element &_element) { try { std::stringstream its_converter; @@ -500,6 +686,7 @@ void configuration_impl::load_application_data( std::size_t its_io_thread_count(VSOMEIP_IO_THREAD_COUNT); std::size_t its_request_debounce_time(VSOMEIP_REQUEST_DEBOUNCE_TIME); std::map<plugin_type_e, std::set<std::string>> plugins; + int its_io_thread_nice_level(VSOMEIP_IO_THREAD_NICE_LEVEL); for (auto i = _tree.begin(); i != _tree.end(); ++i) { std::string its_key(i->first); std::string its_value(i->second.data()); @@ -529,6 +716,9 @@ void configuration_impl::load_application_data( VSOMEIP_WARNING << "Max. number of threads per application is 255"; its_io_thread_count = 255; } + } else if (its_key == "io_thread_nice") { + its_converter << std::dec << its_value; + its_converter >> its_io_thread_nice_level; } else if (its_key == "request_debounce_time") { its_converter << std::dec << its_value; its_converter >> its_request_debounce_time; @@ -552,13 +742,13 @@ void configuration_impl::load_application_data( if (its_inner_key == "application_plugin") { #ifndef _WIN32 library += "."; - library += (VSOMEIP_APPLICATION_PLUGIN_VERSION + '0'); + library += std::to_string(std::uint32_t(VSOMEIP_APPLICATION_PLUGIN_VERSION)); #endif plugins[plugin_type_e::APPLICATION_PLUGIN].insert(library); } else if (its_inner_key == "configuration_plugin") { #ifndef _WIN32 library += "."; - library += (VSOMEIP_PRE_CONFIGURATION_PLUGIN_VERSION + '0'); + library += std::to_string(std::uint32_t(VSOMEIP_PRE_CONFIGURATION_PLUGIN_VERSION)); #endif plugins[plugin_type_e::PRE_CONFIGURATION_PLUGIN].insert(library); } else { @@ -586,7 +776,7 @@ void configuration_impl::load_application_data( applications_[its_name] = std::make_tuple(its_id, its_max_dispatchers, its_max_dispatch_time, its_io_thread_count, - its_request_debounce_time, plugins); + its_request_debounce_time, plugins, its_io_thread_nice_level); } else { VSOMEIP_WARNING << "Multiple configurations for application " << its_name << ". Ignoring a configuration from " @@ -672,56 +862,137 @@ void configuration_impl::load_trace_filters( void configuration_impl::load_trace_filter( const boost::property_tree::ptree &_tree) { - std::shared_ptr<trace_filter_rule> its_filter_rule = std::make_shared<trace_filter_rule>(); - for(auto i = _tree.begin(); i != _tree.end(); ++i) { + std::shared_ptr<trace_filter> its_filter = std::make_shared<trace_filter>(); + bool has_channel(false); + for (auto i = _tree.begin(); i != _tree.end(); ++i) { std::string its_key = i->first; - std::string its_value = i->second.data(); - if(its_key == "channel") { - its_filter_rule->channel_ = its_value; + if (its_key == "channel") { + std::string its_value; + if (i->second.size() == 0) { + its_value = i->second.data(); + its_filter->channels_.push_back(its_value); + } else { + for (auto j = i->second.begin(); j != i->second.end(); ++j) { + its_filter->channels_.push_back(j->second.data()); + } + } + has_channel = true; } else if(its_key == "type") { - its_filter_rule->type_ = its_value; + std::string its_value = i->second.data(); + its_filter->is_positive_ = (its_value == "positive"); } else { - load_trace_filter_expressions(i->second, its_key, its_filter_rule); + load_trace_filter_expressions(i->second, its_key, its_filter); } } - trace_->filter_rules_.push_back(its_filter_rule); + + if (!has_channel) { + its_filter->channels_.push_back("TC"); // default + } + + if (!its_filter->is_range_ || its_filter->matches_.size() == 2) { + trace_->filters_.push_back(its_filter); + } } void configuration_impl::load_trace_filter_expressions( const boost::property_tree::ptree &_tree, std::string &_criteria, - std::shared_ptr<trace_filter_rule> &_filter_rule) { - for(auto i = _tree.begin(); i != _tree.end(); ++i) { - std::string its_value = i->second.data(); - std::stringstream its_converter; - - if(_criteria == "services") { - service_t its_id = NO_TRACE_FILTER_EXPRESSION; - if (its_value.size() > 1 && its_value[0] == '0' && its_value[1] == 'x') { - its_converter << std::hex << its_value; - } else { - its_converter << std::dec << its_value; - } - its_converter >> its_id; - _filter_rule->services_.push_back(its_id); - } else if(_criteria == "methods") { - method_t its_id = NO_TRACE_FILTER_EXPRESSION; - if (its_value.size() > 1 && its_value[0] == '0' && its_value[1] == 'x') { - its_converter << std::hex << its_value; + std::shared_ptr<trace_filter> &_filter) { + if (_criteria == "services") { + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + vsomeip::trace::match_t its_match; + load_trace_filter_match(i->second, its_match); + _filter->matches_.push_back(its_match); + } + } else if (_criteria == "methods") { + if (!has_issued_methods_warning_) { + VSOMEIP_WARNING << "\"method\" entry in filter configuration has no effect!"; + has_issued_methods_warning_ = true; + } + } else if (_criteria == "clients") { + if (!has_issued_clients_warning_) { + VSOMEIP_WARNING << "\"clients\" entry in filter configuration has no effect!"; + has_issued_clients_warning_ = true; + } + } else if (_criteria == "matches") { + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + vsomeip::trace::match_t its_match; + load_trace_filter_match(i->second, its_match); + if (i->first == "from") { + _filter->is_range_ = true; + _filter->matches_.insert(_filter->matches_.begin(), its_match); } else { - its_converter << std::dec << its_value; + if (i->first == "to") _filter->is_range_ = true; + _filter->matches_.push_back(its_match); } - its_converter >> its_id; - _filter_rule->methods_.push_back(its_id); - } else if(_criteria == "clients") { - client_t its_id = NO_TRACE_FILTER_EXPRESSION; - if (its_value.size() > 1 && its_value[0] == '0' && its_value[1] == 'x') { - its_converter << std::hex << its_value; - } else { - its_converter << std::dec << its_value; + } + } +} + +void configuration_impl::load_trace_filter_match( + const boost::property_tree::ptree &_data, + vsomeip::trace::match_t &_match) { + std::stringstream its_converter; + + if (_data.size() == 0) { + std::string its_value(_data.data()); + service_t its_service(ANY_SERVICE); + if (its_value.find("0x") == 0) { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + its_converter >> its_service; + + std::get<0>(_match) = its_service; + std::get<1>(_match) = ANY_INSTANCE; + std::get<2>(_match) = ANY_METHOD; + } else { + std::get<0>(_match) = ANY_SERVICE; + std::get<1>(_match) = ANY_INSTANCE; + std::get<2>(_match) = ANY_METHOD; + + for (auto i = _data.begin(); i != _data.end(); ++i) { + std::string its_value; + + its_converter.str(""); + its_converter.clear(); + + try { + its_value = i->second.data(); + if (its_value == "any") its_value = "0xffff"; + + if (i->first == "service") { + service_t its_service(ANY_SERVICE); + if (its_value.find("0x") == 0) { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + its_converter >> its_service; + std::get<0>(_match) = its_service; + } else if (i->first == "instance") { + instance_t its_instance(ANY_INSTANCE); + if (its_value.find("0x") == 0) { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + its_converter >> its_instance; + std::get<1>(_match) = its_instance; + } else if (i->first == "method") { + method_t its_method(ANY_METHOD); + if (its_value.find("0x") == 0) { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + its_converter >> its_method; + std::get<2>(_match) = its_method; + } + } catch (...) { + // Intentionally left empty } - its_converter >> its_id; - _filter_rule->clients_.push_back(its_id); } } } @@ -741,6 +1012,21 @@ void configuration_impl::load_unicast_address(const element &_element) { } } +void configuration_impl::load_netmask(const element &_element) { + try { + std::string its_value = _element.tree_.get<std::string>("netmask"); + if (is_configured_[ET_NETMASK]) { + VSOMEIP_WARNING << "Multiple definitions for netmask." + "Ignoring definition from " << _element.name_; + } else { + netmask_ = netmask_.from_string(its_value); + is_configured_[ET_NETMASK] = true; + } + } catch (...) { + // intentionally left empty! + } +} + void configuration_impl::load_network(const element &_element) { try { std::string its_value(_element.tree_.get<std::string>("network")); @@ -793,6 +1079,31 @@ void configuration_impl::load_diagnosis_address(const element &_element) { } } +void configuration_impl::load_shutdown_timeout(const element &_element) { + const std::string shutdown_timeout("shutdown_timeout"); + try { + if (_element.tree_.get_child_optional(shutdown_timeout)) { + std::string its_value = _element.tree_.get<std::string>("shutdown_timeout"); + if (is_configured_[ET_SHUTDOWN_TIMEOUT]) { + VSOMEIP_WARNING << "Multiple definitions for shutdown_timeout." + "Ignoring definition from " << _element.name_; + } else { + std::stringstream its_converter; + + if (its_value.size() > 1 && its_value[0] == '0' && its_value[1] == 'x') { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + its_converter >> shutdown_timeout_; + is_configured_[ET_SHUTDOWN_TIMEOUT] = true; + } + } + } catch (...) { + // intentionally left empty + } +} + void configuration_impl::load_service_discovery( const element &_element) { try { @@ -978,6 +1289,7 @@ void configuration_impl::load_delays( } void configuration_impl::load_services(const element &_element) { + std::lock_guard<std::mutex> its_lock(services_mutex_); try { auto its_services = _element.tree_.get_child("services"); for (auto i = its_services.begin(); i != its_services.end(); ++i) @@ -1587,10 +1899,10 @@ void configuration_impl::load_permissions(const element &_element) { std::string its_value(i->second.data()); its_converter << std::oct << its_value; its_converter >> permissions_shm_; - } else if (its_key == "umask") { + } else if (its_key == "permissions-uds") { std::string its_value(i->second.data()); its_converter << std::oct << its_value; - its_converter >> umask_; + its_converter >> permissions_uds_; } } @@ -1636,8 +1948,13 @@ void configuration_impl::load_policies(const element &_element) { } else { check_credentials_ = false; } - } - if (its_security->first == "policies") { + } else if (its_security->first == "allow_remote_clients") { + if (its_security->second.data() == "true") { + allow_remote_clients_ = true; + } else { + allow_remote_clients_ = false; + } + } else if (its_security->first == "policies") { for (auto its_policy = its_security->second.begin(); its_policy != its_security->second.end(); ++its_policy) { load_policy(its_policy->second); @@ -1676,16 +1993,19 @@ void configuration_impl::load_policy(const boost::property_tree::ptree &_tree) { if (firstClient < lastClient && lastClient != ANY_CLIENT) { uint32_t overrides(0); for (client_t c = firstClient; c <= lastClient; ++c) { - if (policies_.find(c) != policies_.end()) { + if (find_client_id_policy(c)) { overrides++; } - policies_[c] = policy; } if (overrides) { - VSOMEIP_INFO << std::hex << "Security configuration: " - << "Client range 0x" << firstClient - << " - 0x" << lastClient << " overrides policy of " - << std::dec << overrides << " clients"; + VSOMEIP_WARNING << std::hex << "Security configuration: " + << "for client range 0x" << firstClient + << " - 0x" << lastClient + << " will be ignored as it would override an already existing policy of " + << std::dec << overrides << " clients!"; + } else { + std::lock_guard<std::mutex> its_lock(policies_mutex_); + policies_[std::make_pair(firstClient, lastClient)] = policy; } has_been_inserted = true; } else { @@ -1699,47 +2019,63 @@ void configuration_impl::load_policy(const boost::property_tree::ptree &_tree) { its_converter << std::hex << value; its_converter >> client; if (client != 0x0) { - if (policies_.find(client) != policies_.end()) { - VSOMEIP_INFO << std::hex << "Security configuration: " - << "Overriding policy for client 0x" << client << "."; + if (find_client_id_policy(client)) { + VSOMEIP_WARNING << std::hex << "Security configuration for client " + << client + << " will be ignored as it would overwrite an already existing policy!"; + } else { + std::lock_guard<std::mutex> its_lock(policies_mutex_); + policies_[std::make_pair(client, client)] = policy; } - policies_[client] = policy; has_been_inserted= true; } } } else if (i->first == "credentials") { std::pair<uint32_t, uint32_t> its_uid_range, its_gid_range; + ranges_t its_uid_ranges, its_gid_ranges; + bool has_uid(false), has_gid(false); + bool has_uid_range(false), has_gid_range(false); for (auto n = i->second.begin(); n != i->second.end(); ++n) { std::string its_key(n->first); std::string its_value(n->second.data()); if (its_key == "uid") { - if (its_value != "any") { - uint32_t its_uid; - std::stringstream its_converter; - its_converter << std::dec << its_value; - its_converter >> its_uid; - std::get<0>(its_uid_range) = its_uid; - std::get<1>(its_uid_range) = its_uid; + if(n->second.data().empty()) { + load_ranges(n->second, its_uid_ranges); + has_uid_range = true; } else { - std::get<0>(its_uid_range) = 0; - std::get<1>(its_uid_range) = 0xFFFFFFFF; + if (its_value != "any") { + uint32_t its_uid; + std::stringstream its_converter; + its_converter << std::dec << its_value; + its_converter >> its_uid; + std::get<0>(its_uid_range) = its_uid; + std::get<1>(its_uid_range) = its_uid; + } else { + std::get<0>(its_uid_range) = 0; + std::get<1>(its_uid_range) = 0xFFFFFFFF; + } + has_uid = true; } - has_uid = true; } else if (its_key == "gid") { - if (its_value != "any") { - uint32_t its_gid; - std::stringstream its_converter; - its_converter << std::dec << its_value; - its_converter >> its_gid; - std::get<0>(its_gid_range) = its_gid; - std::get<1>(its_gid_range) = its_gid; + if(n->second.data().empty()) { + load_ranges(n->second, its_gid_ranges); + has_gid_range = true; } else { - std::get<0>(its_gid_range) = 0; - std::get<1>(its_gid_range) = 0xFFFFFFFF; + if (its_value != "any") { + uint32_t its_gid; + std::stringstream its_converter; + its_converter << std::dec << its_value; + its_converter >> its_gid; + std::get<0>(its_gid_range) = its_gid; + std::get<1>(its_gid_range) = its_gid; + } else { + std::get<0>(its_gid_range) = 0; + std::get<1>(its_gid_range) = 0xFFFFFFFF; + } + has_gid = true; } - has_gid = true; } else if (its_key == "allow" || its_key == "deny") { policy->allow_who_ = (its_key == "allow"); load_credential(n->second, policy->ids_); @@ -1755,10 +2091,13 @@ void configuration_impl::load_policy(const boost::property_tree::ptree &_tree) { policy->allow_who_ = true; policy->ids_.insert(std::make_pair(its_uids, its_gids)); } - + if (has_uid_range && has_gid_range) { + policy->allow_who_ = true; + policy->ids_.insert(std::make_pair(its_uid_ranges, its_gid_ranges)); + } } else if (i->first == "allow") { if (allow_deny_set) { - VSOMEIP_WARNING << "Security configuration: \"allow\" tag overrides " + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: \"allow\" tag overrides " << "already set \"deny\" tag. " << "Either \"deny\" or \"allow\" is allowed."; } @@ -1769,49 +2108,98 @@ void configuration_impl::load_policy(const boost::property_tree::ptree &_tree) { for (auto n = l->second.begin(); n != l->second.end(); ++n) { service_t service = 0x0; instance_t instance = 0x0; + ids_t its_instance_method_ranges; for (auto k = n->second.begin(); k != n->second.end(); ++k) { std::stringstream its_converter; if (k->first == "service") { std::string value = k->second.data(); its_converter << std::hex << value; its_converter >> service; - } else if (k->first == "instance") { + } else if (k->first == "instance") { // legacy definition for instances + ranges_t its_instance_ranges; + ranges_t its_method_ranges; std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> instance; + if (value != "any") { + its_converter << std::hex << value; + its_converter >> instance; + if (instance != 0x0) { + its_instance_ranges.insert(std::make_pair(instance, instance)); + its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); + } + } else { + its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); + its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); + } + its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); + } else if (k->first == "instances") { // new instances definition + for (auto p = k->second.begin(); p != k->second.end(); ++p) { + ranges_t its_instance_ranges; + ranges_t its_method_ranges; + for (auto m = p->second.begin(); m != p->second.end(); ++m) { + if (m->first == "ids") { + load_instance_ranges(m->second, its_instance_ranges); + } else if (m->first == "methods") { + load_instance_ranges(m->second, its_method_ranges); + } + if (!its_instance_ranges.empty() && !its_method_ranges.empty()) { + its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); + } + } + } + if (its_instance_method_ranges.empty()) { + ranges_t its_legacy_instance_ranges; + ranges_t its_legacy_method_ranges; + its_legacy_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); + // try to only load instance ranges with any method to be allowed + load_instance_ranges(k->second, its_legacy_instance_ranges); + if (!its_legacy_instance_ranges.empty() && !its_legacy_method_ranges.empty()) { + its_instance_method_ranges.insert(std::make_pair(its_legacy_instance_ranges, + its_legacy_method_ranges)); + } + } } } - if (service != 0x0 && instance != 0x0) { + if (service != 0x0 && !its_instance_method_ranges.empty()) { policy->services_.insert( - std::make_pair(service, instance)); + std::make_pair(service, its_instance_method_ranges)); } } } else if (l->first == "offers") { for (auto n = l->second.begin(); n != l->second.end(); ++n) { service_t service = 0x0; instance_t instance = 0x0; + ranges_t its_instance_ranges; for (auto k = n->second.begin(); k != n->second.end(); ++k) { std::stringstream its_converter; if (k->first == "service") { std::string value = k->second.data(); its_converter << std::hex << value; its_converter >> service; - } else if (k->first == "instance") { + } else if (k->first == "instance") { // legacy definition for instances std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> instance; + if (value != "any") { + its_converter << std::hex << value; + its_converter >> instance; + if (instance != 0x0) { + its_instance_ranges.insert(std::make_pair(instance, instance)); + } + } else { + its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); + } + } else if (k->first == "instances") { // new instances definition + load_instance_ranges(k->second, its_instance_ranges); } } - if (service != 0x0 && instance != 0x0) { + if (service != 0x0 && !its_instance_ranges.empty()) { policy->offers_.insert( - std::make_pair(service, instance)); + std::make_pair(service, its_instance_ranges)); } } } } } else if (i->first == "deny") { if (allow_deny_set) { - VSOMEIP_WARNING << "Security configuration: \"deny\" tag overrides " + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: \"deny\" tag overrides " << "already set \"allow\" tag. " << "Either \"deny\" or \"allow\" is allowed."; } @@ -1822,21 +2210,60 @@ void configuration_impl::load_policy(const boost::property_tree::ptree &_tree) { for (auto n = l->second.begin(); n != l->second.end(); ++n) { service_t service = 0x0; instance_t instance = 0x0; + ids_t its_instance_method_ranges; for (auto k = n->second.begin(); k != n->second.end(); ++k) { std::stringstream its_converter; if (k->first == "service") { std::string value = k->second.data(); its_converter << std::hex << value; its_converter >> service; - } else if (k->first == "instance") { + } else if (k->first == "instance") { // legacy definition for instances + ranges_t its_instance_ranges; + ranges_t its_method_ranges; std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> instance; + if (value != "any") { + its_converter << std::hex << value; + its_converter >> instance; + if (instance != 0x0) { + its_instance_ranges.insert(std::make_pair(instance, instance)); + its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); + } + } else { + its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); + its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); + } + its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); + } else if (k->first == "instances") { // new instances definition + for (auto p = k->second.begin(); p != k->second.end(); ++p) { + ranges_t its_instance_ranges; + ranges_t its_method_ranges; + for (auto m = p->second.begin(); m != p->second.end(); ++m) { + if (m->first == "ids") { + load_instance_ranges(m->second, its_instance_ranges); + } else if (m->first == "methods") { + load_instance_ranges(m->second, its_method_ranges); + } + if (!its_instance_ranges.empty() && !its_method_ranges.empty()) { + its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); + } + } + } + if (its_instance_method_ranges.empty()) { + ranges_t its_legacy_instance_ranges; + ranges_t its_legacy_method_ranges; + its_legacy_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); + // try to only load instance ranges with any method to be allowed + load_instance_ranges(k->second, its_legacy_instance_ranges); + if (!its_legacy_instance_ranges.empty() && !its_legacy_method_ranges.empty()) { + its_instance_method_ranges.insert(std::make_pair(its_legacy_instance_ranges, + its_legacy_method_ranges)); + } + } } } - if (service != 0x0 && instance != 0x0) { + if (service != 0x0 && !its_instance_method_ranges.empty()) { policy->services_.insert( - std::make_pair(service, instance)); + std::make_pair(service, its_instance_method_ranges)); } } } @@ -1844,21 +2271,31 @@ void configuration_impl::load_policy(const boost::property_tree::ptree &_tree) { for (auto n = l->second.begin(); n != l->second.end(); ++n) { service_t service = 0x0; instance_t instance = 0x0; + ranges_t its_instance_ranges; for (auto k = n->second.begin(); k != n->second.end(); ++k) { std::stringstream its_converter; if (k->first == "service") { std::string value = k->second.data(); its_converter << std::hex << value; its_converter >> service; - } else if (k->first == "instance") { + } else if (k->first == "instance") { // legacy definition for instances std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> instance; + if (value != "any") { + its_converter << std::hex << value; + its_converter >> instance; + if (instance != 0x0) { + its_instance_ranges.insert(std::make_pair(instance, instance)); + } + } else { + its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); + } + } else if (k->first == "instances") { // new instances definition + load_instance_ranges(k->second, its_instance_ranges); } } - if (service != 0x0 && instance != 0x0) { + if (service != 0x0 && !its_instance_ranges.empty()) { policy->offers_.insert( - std::make_pair(service, instance)); + std::make_pair(service, its_instance_ranges)); } } } @@ -1867,6 +2304,7 @@ void configuration_impl::load_policy(const boost::property_tree::ptree &_tree) { } if (!has_been_inserted) { + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); any_client_policies_.push_back(policy); } } @@ -1882,7 +2320,7 @@ void configuration_impl::load_credential( } else if (its_key == "gid") { load_ranges(j->second, its_gid_ranges); } else { - VSOMEIP_WARNING << "Security configuration: " + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " << "Malformed credential (contains illegal key \"" << its_key << "\""; } @@ -1892,6 +2330,41 @@ void configuration_impl::load_credential( } } +void configuration_impl::load_security_update_whitelist(const element &_element) { +#ifdef _WIN32 + return; +#endif + try { + auto optional = _element.tree_.get_child_optional("security-update-whitelist"); + if (!optional) { + return; + } + auto found_whitelist = _element.tree_.get_child("security-update-whitelist"); + for (auto its_whitelist = found_whitelist.begin(); + its_whitelist != found_whitelist.end(); ++its_whitelist) { + + if (its_whitelist->first == "uids") { + { + std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); + load_ranges(its_whitelist->second, uid_whitelist_); + } + } else if (its_whitelist->first == "services") { + { + std::lock_guard<std::mutex> its_lock(service_interface_whitelist_mutex_); + load_service_ranges(its_whitelist->second, service_interface_whitelist_); + } + } else if (its_whitelist->first == "check-whitelist") { + if (its_whitelist->second.data() == "true") { + check_whitelist_ = true; + } else { + check_whitelist_ = false; + } + } + } + } catch (...) { + } +} + void configuration_impl::load_ranges( const boost::property_tree::ptree &_tree, ranges_t &_range) { ranges_t its_ranges; @@ -1902,7 +2375,6 @@ void configuration_impl::load_ranges( std::stringstream its_converter; its_converter << std::dec << its_data.data(); its_converter >> its_id; - its_ranges.insert(std::make_pair(its_id, its_id)); } else { uint32_t its_first, its_last; @@ -1929,7 +2401,7 @@ void configuration_impl::load_ranges( } has_last = true; } else { - VSOMEIP_WARNING << "Security configuration: " + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " << " Malformed range. Contains illegal key (" << its_key << ")"; } @@ -1944,6 +2416,128 @@ void configuration_impl::load_ranges( _range = its_ranges; } +void configuration_impl::load_instance_ranges( + const boost::property_tree::ptree &_tree, ranges_t &_range) { + ranges_t its_ranges; + std::string key(_tree.data()); + if (key == "any") { + its_ranges.insert(std::make_pair(0x01, 0xFFFF)); + _range = its_ranges; + return; + } + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + auto its_data = i->second; + if (!its_data.data().empty()) { + uint32_t its_id = 0x0; + std::stringstream its_converter; + its_converter << std::hex << its_data.data(); + its_converter >> its_id; + if (its_id != 0x0) { + its_ranges.insert(std::make_pair(its_id, its_id)); + } + } else { + uint32_t its_first, its_last; + bool has_first(false), has_last(false); + for (auto j = its_data.begin(); j != its_data.end(); ++j) { + std::string its_key(j->first); + std::string its_value(j->second.data()); + if (its_key == "first") { + if (its_value == "max") { + its_first = 0xFFFF; + } else { + std::stringstream its_converter; + its_converter << std::hex << j->second.data(); + its_converter >> its_first; + } + has_first = true; + } else if (its_key == "last") { + if (its_value == "max") { + its_last = 0xFFFF; + } else { + std::stringstream its_converter; + its_converter << std::hex << j->second.data(); + its_converter >> its_last; + } + has_last = true; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " + << " Malformed range. Contains illegal key (" + << its_key << ")"; + } + } + + if (has_first && has_last) { + if( its_last > its_first) { + its_ranges.insert(std::make_pair(its_first, its_last)); + } + } + } + } + + _range = its_ranges; +} + +void configuration_impl::load_service_ranges( + const boost::property_tree::ptree &_tree, std::set<std::pair<service_t, service_t>> &_ranges) { + std::set<std::pair<service_t, service_t>> its_ranges; + std::string key(_tree.data()); + if (key == "any") { + its_ranges.insert(std::make_pair(0x01, 0xFFFF)); + _ranges = its_ranges; + return; + } + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + auto its_data = i->second; + if (!its_data.data().empty()) { + service_t its_id = 0x0; + std::stringstream its_converter; + its_converter << std::hex << its_data.data(); + its_converter >> its_id; + if (its_id != 0x0) { + its_ranges.insert(std::make_pair(its_id, its_id)); + } + } else { + service_t its_first, its_last; + bool has_first(false), has_last(false); + for (auto j = its_data.begin(); j != its_data.end(); ++j) { + std::string its_key(j->first); + std::string its_value(j->second.data()); + if (its_key == "first") { + if (its_value == "max") { + its_first = 0xFFFF; + } else { + std::stringstream its_converter; + its_converter << std::hex << j->second.data(); + its_converter >> its_first; + } + has_first = true; + } else if (its_key == "last") { + if (its_value == "max") { + its_last = 0xFFFF; + } else { + std::stringstream its_converter; + its_converter << std::hex << j->second.data(); + its_converter >> its_last; + } + has_last = true; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Security interface whitelist configuration: " + << " Malformed range. Contains illegal key (" + << its_key << ")"; + } + } + + if (has_first && has_last) { + if( its_last >= its_first) { + its_ranges.insert(std::make_pair(its_first, its_last)); + } + } + } + } + + _ranges = its_ranges; +} + /////////////////////////////////////////////////////////////////////////////// // Internal helper /////////////////////////////////////////////////////////////////////////////// @@ -1994,6 +2588,10 @@ const boost::asio::ip::address & configuration_impl::get_unicast_address() const return unicast_; } +const boost::asio::ip::address& configuration_impl::get_netmask() const { + return netmask_; +} + unsigned short configuration_impl::get_diagnosis_address() const { return diagnosis_; } @@ -2046,8 +2644,9 @@ std::string configuration_impl::get_unicast_address(service_t _service, uint16_t configuration_impl::get_reliable_port(service_t _service, instance_t _instance) const { + std::lock_guard<std::mutex> its_lock(services_mutex_); uint16_t its_reliable(ILLEGAL_PORT); - auto its_service = find_service(_service, _instance); + auto its_service = find_service_unlocked(_service, _instance); if (its_service) its_reliable = its_service->reliable_; @@ -2056,8 +2655,9 @@ uint16_t configuration_impl::get_reliable_port(service_t _service, uint16_t configuration_impl::get_unreliable_port(service_t _service, instance_t _instance) const { + std::lock_guard<std::mutex> its_lock(services_mutex_); uint16_t its_unreliable = ILLEGAL_PORT; - auto its_service = find_service(_service, _instance); + auto its_service = find_service_unlocked(_service, _instance); if (its_service) its_unreliable = its_service->unreliable_; @@ -2174,6 +2774,17 @@ std::size_t configuration_impl::get_io_thread_count(const std::string &_name) co return its_io_thread_count; } +int configuration_impl::get_io_thread_nice_level(const std::string &_name) const { + int its_io_thread_nice_level = VSOMEIP_IO_THREAD_NICE_LEVEL; + + auto found_application = applications_.find(_name); + if (found_application != applications_.end()) { + its_io_thread_nice_level = std::get<6>(found_application->second); + } + + return its_io_thread_nice_level; +} + std::size_t configuration_impl::get_max_dispatchers( const std::string &_name) const { std::size_t its_max_dispatchers = VSOMEIP_MAX_DISPATCHERS; @@ -2200,6 +2811,7 @@ std::size_t configuration_impl::get_max_dispatch_time( std::set<std::pair<service_t, instance_t> > configuration_impl::get_remote_services() const { + std::lock_guard<std::mutex> its_lock(services_mutex_); std::set<std::pair<service_t, instance_t> > its_remote_services; for (auto i : services_) { for (auto j : i.second) { @@ -2335,8 +2947,9 @@ bool configuration_impl::find_port(uint16_t &_port, uint16_t _remote, bool _reli bool configuration_impl::is_event_reliable(service_t _service, instance_t _instance, event_t _event) const { + std::lock_guard<std::mutex> its_lock(services_mutex_); bool is_reliable(false); - auto its_service = find_service(_service, _instance); + auto its_service = find_service_unlocked(_service, _instance); if (its_service) { auto its_event = its_service->events_.find(_event); if (its_event != its_service->events_.end()) { @@ -2353,6 +2966,12 @@ bool configuration_impl::is_event_reliable(service_t _service, std::shared_ptr<service> configuration_impl::find_service(service_t _service, instance_t _instance) const { + std::lock_guard<std::mutex> its_lock(services_mutex_); + return find_service_unlocked(_service, _instance); +} + +std::shared_ptr<service> configuration_impl::find_service_unlocked(service_t _service, + instance_t _instance) const { std::shared_ptr<service> its_service; auto find_service = services_.find(_service); if (find_service != services_.end()) { @@ -2517,8 +3136,8 @@ uint32_t configuration_impl::get_watchdog_timeout() const { uint32_t configuration_impl::get_allowed_missing_pongs() const { return watchdog_->missing_pongs_allowed_; } -std::uint32_t configuration_impl::get_umask() const { - return umask_; +std::uint32_t configuration_impl::get_permissions_uds() const { + return permissions_uds_; } std::uint32_t configuration_impl::get_permissions_shm() const { @@ -2529,24 +3148,25 @@ bool configuration_impl::is_security_enabled() const { return policy_enabled_; } +bool configuration_impl::is_audit_mode_enabled() const { + return check_credentials_; +} + bool configuration_impl::check_credentials(client_t _client, uint32_t _uid, uint32_t _gid) { if (!policy_enabled_) { return true; } - // store the client -> (uid, gid) mapping - ids_[_client] = std::make_pair(_uid, _gid); - std::vector<std::shared_ptr<policy> > its_policies; bool has_id(false); - auto its_client = policies_.find(_client); - - // Use client specific policy if it does exist - if (its_client != policies_.end()) - its_policies.push_back(its_client->second); - else + auto found_policy = find_client_id_policy(_client); + if (found_policy) { + its_policies.push_back(found_policy); + } else { + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); its_policies = any_client_policies_; + } for (const auto &p : its_policies) { for (auto its_credential : p->ids_) { @@ -2571,21 +3191,38 @@ bool configuration_impl::check_credentials(client_t _client, uint32_t _uid, } if ((has_id && p->allow_who_) || (!has_id && !p->allow_who_)) { + if (!store_client_to_uid_gid_mapping(_client,_uid, _gid)) { + std::string security_mode_text = "!"; + if (!check_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " with UID/GID=" << std::dec << _uid << "/" << _gid + << " : Check credentials failed as existing credentials would be overwritten" + << security_mode_text; + + return !check_credentials_; + } + store_uid_gid_to_client_mapping(_uid, _gid, _client); return true; } } + std::string security_mode_text = " ~> Skip!"; if (!check_credentials_) { - VSOMEIP_INFO << "vSomeIP Security: Check credentials failed for client 0x" - << std::hex << _client << " with UID/GID=" << std::dec << _uid - << "/" << _gid << " but will be allowed due to audit mode is active!"; + security_mode_text = " but will be allowed due to audit mode is active!"; } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " with UID/GID=" << std::dec << _uid << "/" << _gid + << " : Check credentials failed" << security_mode_text; + return !check_credentials_; } bool configuration_impl::is_client_allowed(client_t _client, service_t _service, - instance_t _instance) const { + instance_t _instance, method_t _method, bool _is_request_service) const { if (!policy_enabled_) { return true; } @@ -2593,35 +3230,39 @@ bool configuration_impl::is_client_allowed(client_t _client, service_t _service, uint32_t its_uid(0xffffffff), its_gid(0xffffffff); bool must_apply(true); std::vector<std::shared_ptr<policy> > its_policies; - auto its_client = policies_.find(_client); - - // Use client specific policy if it does exist - if (its_client != policies_.end()) - its_policies.push_back(its_client->second); + auto found_policy = find_client_id_policy(_client); + if (found_policy) + its_policies.push_back(found_policy); else { must_apply = false; - its_policies = any_client_policies_; + { + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); + its_policies = any_client_policies_; + } + std::lock_guard<std::mutex> its_lock(ids_mutex_); auto found_id = ids_.find(_client); if (found_id != ids_.end()) { its_uid = found_id->second.first; its_gid = found_id->second.second; } else { + std::string security_mode_text = " ~> Skip!"; if (!check_credentials_) { - VSOMEIP_INFO << "vSomeIP Security: Cannot determine uid/gid for" - "client 0x" << std::hex << _client - << ". Therefore it isn't allowed to communicate to service/instance " - << _service << "/" << _instance - << " but will be allowed due to audit mode is active!"; + security_mode_text = " but will be allowed due to audit mode is active!"; } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " : Cannot determine uid/gid. Therefore it isn't allowed to communicate to service/instance " + << _service << "/" << _instance + << security_mode_text; + return !check_credentials_; } } for (const auto &p : its_policies) { - bool has_uid(false), has_gid(false); + bool has_uid(false), has_gid(false), has_service(false), has_instance_id(false), has_method_id(false); if (must_apply) { - has_uid = has_gid = p->allow_what_; + has_uid = has_gid = p->allow_who_; } else { for (auto its_credential : p->ids_) { has_uid = has_gid = false; @@ -2643,21 +3284,68 @@ bool configuration_impl::is_client_allowed(client_t _client, service_t _service, } } - auto its_service = p->services_.find(std::make_pair(_service, _instance)); - if (has_uid && has_gid && p->allow_what_ && its_service != p->services_.end()) { - return true; - } else if (!has_uid && !has_gid && !p->allow_what_ && its_service == p->services_.end()) { - return true; + for (auto its_offer : p->services_) { + if (std::get<0>(its_offer) == _service) { + for (auto its_ids : std::get<1>(its_offer)) { + has_service = has_instance_id = has_method_id = false; + for (auto its_instance_range : std::get<0>(its_ids)) { + if (std::get<0>(its_instance_range) <= _instance && _instance <= std::get<1>(its_instance_range)) { + has_instance_id = true; + break; + } + } + if (!_is_request_service) { + for (auto its_method_range : std::get<1>(its_ids)) { + if (std::get<0>(its_method_range) <= _method && _method <= std::get<1>(its_method_range)) { + has_method_id = true; + break; + } + } + } else { + // handle VSOMEIP_REQUEST_SERVICE + has_method_id = true; + } + + if (has_instance_id && has_method_id) { + has_service = true; + break; + } + } + if (has_service) + break; + } + } + + if ((has_uid && has_gid && p->allow_who_) || ((!has_uid || !has_gid) && !p->allow_who_)) { + if (p->allow_what_) { + // allow policy + if (has_service) { + return true; + } + } else { + // deny policy + // allow client if the service / instance / !ANY_METHOD was not found + if ((!has_service && (_method != ANY_METHOD)) + // allow client if the service / instance / ANY_METHOD was not found + // and it is a "deny nothing" policy + || (!has_service && (_method == ANY_METHOD) && p->services_.empty())) { + return true; + } + } } } + std::string security_mode_text = " ~> Skip!"; if (!check_credentials_) { - VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client - << " isn't allowed to communicate with service/instance " - << _service << "/" << _instance - << " but will be allowed due to audit mode is active!"; + security_mode_text = " but will be allowed due to audit mode is active!"; } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " with UID/GID=" << std::dec << its_uid << "/" << its_gid + << " : Isn't allowed to communicate with service/instance/(method / event) " << std::hex + << _service << "/" << _instance << "/" << _method + << security_mode_text; + return !check_credentials_; } @@ -2670,35 +3358,39 @@ bool configuration_impl::is_offer_allowed(client_t _client, service_t _service, uint32_t its_uid(0xffffffff), its_gid(0xffffffff); bool must_apply(true); std::vector<std::shared_ptr<policy> > its_policies; - auto its_client = policies_.find(_client); - - // Use client specific policy if it does exist - if (its_client != policies_.end()) - its_policies.push_back(its_client->second); + auto found_policy = find_client_id_policy(_client); + if (found_policy) + its_policies.push_back(found_policy); else { must_apply = false; - its_policies = any_client_policies_; + { + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); + its_policies = any_client_policies_; + } + std::lock_guard<std::mutex> its_lock(ids_mutex_); auto found_id = ids_.find(_client); if (found_id != ids_.end()) { its_uid = found_id->second.first; its_gid = found_id->second.second; } else { + std::string audit_mode_text = " ~> Skip offer!"; if (!check_credentials_) { - VSOMEIP_INFO << "vSomeIP Security: Cannot determine uid/gid for" - "client 0x" << std::hex << _client - << ". Therefore it isn't allowed to offer service/instance " - << _service << "/" << _instance - << " but will be allowed due to audit mode is active!"; + audit_mode_text = " but will be allowed due to audit mode is active!"; } + + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " : Cannot determine uid/gid. Therefore it isn't allowed to offer service/instance " + << _service << "/" << _instance << audit_mode_text; + return !check_credentials_; } } for (const auto &p : its_policies) { - bool has_uid(false), has_gid(false); + bool has_uid(false), has_gid(false), has_offer(false); if (must_apply) { - has_uid = has_gid = p->allow_what_; + has_uid = has_gid = p->allow_who_; } else { for (auto its_credential : p->ids_) { has_uid = has_gid = false; @@ -2720,26 +3412,152 @@ bool configuration_impl::is_offer_allowed(client_t _client, service_t _service, } } - auto its_offer = p->offers_.find(std::make_pair(_service, _instance)); - if (has_uid && has_gid - && p->allow_what_ && its_offer != p->offers_.end()) { - return true; - } else if (!has_uid && !has_gid - && !p->allow_what_ && its_offer == p->offers_.end()) { - return true; + for (auto its_offer : p->offers_) { + has_offer = false; + if (std::get<0>(its_offer) == _service) { + for (auto its_instance_range : std::get<1>(its_offer)) { + if (std::get<0>(its_instance_range) <= _instance && _instance <= std::get<1>(its_instance_range)) { + has_offer = true; + break; + } + } + if (has_offer) + break; + } + } + + if ((has_uid && has_gid && p->allow_who_) || ((!has_uid || !has_gid) && !p->allow_who_)) { + if (p->allow_what_ == has_offer) { + return true; + } } } + std::string security_mode_text = " ~> Skip offer!"; if (!check_credentials_) { - VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client - << " isn't allowed to offer service/instance " - << _service << "/" << _instance - << " but will be allowed due to audit mode is active!"; + security_mode_text = " but will be allowed due to audit mode is active!"; } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " with UID/GID=" << std::dec << its_uid << "/" << its_gid + << " isn't allowed to offer service/instance " << std::hex + << _service << "/" << _instance + << security_mode_text; + + return !check_credentials_; } +bool configuration_impl::store_client_to_uid_gid_mapping(client_t _client, + uint32_t _uid, uint32_t _gid) { + { + // store the client -> (uid, gid) mapping + std::lock_guard<std::mutex> its_lock(ids_mutex_); + auto found_client = ids_.find(_client); + if (found_client != ids_.end()) { + if (found_client->second != std::make_pair(_uid, _gid)) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" + << std::hex << _client << " with UID/GID=" + << std::dec << _uid << "/" << _gid << " : Overwriting existing credentials UID/GID=" + << std::dec << std::get<0>(found_client->second) << "/" + << std::get<1>(found_client->second); + found_client->second = std::make_pair(_uid, _gid); + return true; + } + } else { + ids_[_client] = std::make_pair(_uid, _gid); + } + return true; + } +} + +bool configuration_impl::get_client_to_uid_gid_mapping(client_t _client, std::pair<uint32_t, uint32_t> &_uid_gid) { + { + // get the UID / GID of the client + std::lock_guard<std::mutex> its_lock(ids_mutex_); + if (ids_.find(_client) != ids_.end()) { + _uid_gid = ids_[_client]; + return true; + } + return false; + } +} + +bool configuration_impl::remove_client_to_uid_gid_mapping(client_t _client) { + std::pair<uint32_t, uint32_t> its_uid_gid; + bool client_removed(false); + bool uid_gid_removed(false); + { + std::lock_guard<std::mutex> its_lock(ids_mutex_); + auto found_client = ids_.find(_client); + if (found_client != ids_.end()) { + its_uid_gid = found_client->second; + ids_.erase(found_client); + client_removed = true; + } + } + { + std::lock_guard<std::mutex> its_lock(uid_to_clients_mutex_); + if (client_removed) { + auto found_uid_gid = uid_to_clients_.find(its_uid_gid); + if (found_uid_gid != uid_to_clients_.end()) { + auto its_client = found_uid_gid->second.find(_client); + if (its_client != found_uid_gid->second.end()) { + found_uid_gid->second.erase(its_client); + if (found_uid_gid->second.empty()) { + uid_to_clients_.erase(found_uid_gid); + } + uid_gid_removed = true; + } + } + } else { + for (auto its_uid_gid = uid_to_clients_.begin(); + its_uid_gid != uid_to_clients_.end(); ++its_uid_gid) { + auto its_client = its_uid_gid->second.find(_client); + if (its_client != its_uid_gid->second.end()) { + its_uid_gid->second.erase(its_client); + if (its_uid_gid->second.empty()) { + uid_to_clients_.erase(its_uid_gid); + } + uid_gid_removed = true; + break; + } + } + } + } + return (client_removed && uid_gid_removed); +} + +void configuration_impl::store_uid_gid_to_client_mapping(uint32_t _uid, uint32_t _gid, + client_t _client) { + { + // store the uid gid to clients mapping + std::lock_guard<std::mutex> its_lock(uid_to_clients_mutex_); + std::set<client_t> mapped_clients; + if (uid_to_clients_.find(std::make_pair(_uid, _gid)) != uid_to_clients_.end()) { + mapped_clients = uid_to_clients_[std::make_pair(_uid, _gid)]; + mapped_clients.insert(_client); + uid_to_clients_[std::make_pair(_uid, _gid)] = mapped_clients; + } else { + mapped_clients.insert(_client); + uid_to_clients_[std::make_pair(_uid, _gid)] = mapped_clients; + } + } +} + +bool configuration_impl::get_uid_gid_to_client_mapping(std::pair<uint32_t, uint32_t> _uid_gid, + std::set<client_t> &_clients) { + { + // get the clients corresponding to uid, gid + std::lock_guard<std::mutex> its_lock(uid_to_clients_mutex_); + if (uid_to_clients_.find(_uid_gid) != uid_to_clients_.end()) { + _clients = uid_to_clients_[_uid_gid]; + return true; + } + return false; + } +} + std::map<plugin_type_e, std::set<std::string>> configuration_impl::get_plugins( const std::string &_name) const { std::map<plugin_type_e, std::set<std::string>> result; @@ -2793,8 +3611,8 @@ void configuration_impl::load_e2e_protected(const boost::property_tree::ptree &_ uint16_t data_id(0); std::string variant(""); std::string profile(""); - uint16_t service_id(0); - uint16_t event_id(0); + service_t service_id(0); + event_t event_id(0); uint16_t crc_offset(0); uint8_t data_id_mode(0); @@ -2861,8 +3679,7 @@ void configuration_impl::load_e2e_protected(const boost::property_tree::ptree &_ its_converter >> data_length; } } - e2exf::data_identifier its_data_identifier = {service_id, event_id}; - e2e_configuration_[its_data_identifier] = std::make_shared<cfg::e2e>( + e2e_configuration_[std::make_pair(service_id, event_id)] = std::make_shared<cfg::e2e>( data_id, variant, profile, @@ -2876,7 +3693,7 @@ void configuration_impl::load_e2e_protected(const boost::property_tree::ptree &_ ); } -std::map<e2exf::data_identifier, std::shared_ptr<cfg::e2e>> configuration_impl::get_e2e_configuration() const { +std::map<e2exf::data_identifier_t, std::shared_ptr<cfg::e2e>> configuration_impl::get_e2e_configuration() const { return e2e_configuration_; } @@ -3217,6 +4034,55 @@ void configuration_impl::load_event_debounce_ignore( } } +void configuration_impl::load_offer_acceptance_required( + const element &_element) { + const std::string oar("offer_acceptance_required"); + try { + std::lock_guard<std::mutex> its_lock(offer_acceptance_required_ips_mutex_); + if (_element.tree_.get_child_optional(oar)) { + if (is_configured_[ET_OFFER_ACCEPTANCE_REQUIRED]) { + VSOMEIP_WARNING << "Multiple definitions of " << oar + << " Ignoring definition from " << _element.name_; + } else { + for (const auto& ipe : _element.tree_.get_child(oar)) { + boost::system::error_code ec; + boost::asio::ip::address its_address = + boost::asio::ip::address::from_string(ipe.first.data(), ec); + if (!its_address.is_unspecified()) { + offer_acceptance_required_ips_[its_address] = ipe.second.data(); + } + } + is_configured_[ET_OFFER_ACCEPTANCE_REQUIRED] = true; + } + } + } catch (...) { + // intentionally left empty + } +} + +void configuration_impl::load_udp_receive_buffer_size(const element &_element) { + const std::string urbs("udp-receive-buffer-size"); + try { + if (_element.tree_.get_child_optional(urbs)) { + if (is_configured_[ET_UDP_RECEIVE_BUFFER_SIZE]) { + VSOMEIP_WARNING << "Multiple definitions of " << urbs + << " Ignoring definition from " << _element.name_; + } else { + const std::string s(_element.tree_.get_child(urbs).data()); + try { + udp_receive_buffer_size_ = static_cast<std::uint32_t>(std::stoul( + s.c_str(), NULL, 10)); + } catch (const std::exception &e) { + VSOMEIP_ERROR<< __func__ << ": " << urbs << " " << e.what(); + } + is_configured_[ET_UDP_RECEIVE_BUFFER_SIZE] = true; + } + } + } catch (...) { + // intentionally left empty + } +} + std::shared_ptr<debounce> configuration_impl::get_debounce( service_t _service, instance_t _instance, event_t _event) const { auto found_service = debounces_.find(_service); @@ -3232,5 +4098,296 @@ std::shared_ptr<debounce> configuration_impl::get_debounce( return nullptr; } +void configuration_impl::load_tcp_restart_settings(const element &_element) { + const std::string tcp_restart_aborts_max("tcp-restart-aborts-max"); + const std::string tcp_connect_time_max("tcp-connect-time-max"); + + try { + if (_element.tree_.get_child_optional(tcp_restart_aborts_max)) { + if (is_configured_[ET_TCP_RESTART_ABORTS_MAX]) { + VSOMEIP_WARNING << "Multiple definitions for " + << tcp_restart_aborts_max + << " Ignoring definition from " << _element.name_; + } else { + is_configured_[ET_TCP_RESTART_ABORTS_MAX] = true; + auto mpsl = _element.tree_.get_child( + tcp_restart_aborts_max); + std::string s(mpsl.data()); + try { + tcp_restart_aborts_max_ = + static_cast<std::uint32_t>(std::stoul( + s.c_str(), NULL, 10)); + } catch (const std::exception &e) { + VSOMEIP_ERROR<<__func__ << ": " << tcp_restart_aborts_max + << " " << e.what(); + } + } + } + if (_element.tree_.get_child_optional(tcp_connect_time_max)) { + if (is_configured_[ET_TCP_CONNECT_TIME_MAX]) { + VSOMEIP_WARNING << "Multiple definitions for " + << tcp_connect_time_max + << " Ignoring definition from " << _element.name_; + } else { + is_configured_[ET_TCP_CONNECT_TIME_MAX] = true; + auto mpsl = _element.tree_.get_child(tcp_connect_time_max); + std::string s(mpsl.data()); + try { + tcp_connect_time_max_= + static_cast<std::uint32_t>( + std::stoul(s.c_str(), NULL, 10)); + } catch (const std::exception &e) { + VSOMEIP_ERROR<< __func__ << ": "<< tcp_connect_time_max + << " " << e.what(); + } + } + } + } catch (...) { + } +} + +std::uint32_t configuration_impl::get_max_tcp_restart_aborts() const { + return tcp_restart_aborts_max_; +} + +std::uint32_t configuration_impl::get_max_tcp_connect_time() const { + return tcp_connect_time_max_; +} + +bool configuration_impl::offer_acceptance_required( + const boost::asio::ip::address& _address) const { + std::lock_guard<std::mutex> its_lock(offer_acceptance_required_ips_mutex_); + return offer_acceptance_required_ips_.find(_address) + != offer_acceptance_required_ips_.end(); +} + +void configuration_impl::set_offer_acceptance_required( + const boost::asio::ip::address& _address, const std::string& _path, + bool _enable) { + std::lock_guard<std::mutex> its_lock(offer_acceptance_required_ips_mutex_); + if (_enable) { + const auto found_address = offer_acceptance_required_ips_.find(_address); + if (found_address != offer_acceptance_required_ips_.end()) { + boost::system::error_code ec; + VSOMEIP_WARNING << __func__ << " configuration for: " + << found_address->first.to_string(ec) << " -> " + << found_address->second << " already configured." + << " Won't update with: "<< _path; + } else { + offer_acceptance_required_ips_[_address] = _path; + } + } else { + offer_acceptance_required_ips_.erase(_address); + } +} + +std::map<boost::asio::ip::address, std::string> +configuration_impl::get_offer_acceptance_required() { + std::lock_guard<std::mutex> its_lock(offer_acceptance_required_ips_mutex_); + return offer_acceptance_required_ips_; +} + +std::uint32_t configuration_impl::get_udp_receive_buffer_size() const { + return udp_receive_buffer_size_; +} + +std::shared_ptr<policy> configuration_impl::find_client_id_policy(client_t _client) const { + std::lock_guard<std::mutex> its_lock(policies_mutex_); + for (auto client_id_pair : policies_) { + if (std::get<0>(client_id_pair.first) <= _client + && _client <= std::get<1>(client_id_pair.first)) { + return client_id_pair.second; + } + } + return nullptr; +} + +bool configuration_impl::remove_security_policy(uint32_t _uid, uint32_t _gid) { + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); + bool was_removed(false); + if (!any_client_policies_.empty()) { + std::vector<std::shared_ptr<policy>>::iterator p_it = any_client_policies_.begin(); + while (p_it != any_client_policies_.end()) { + bool has_uid(false), has_gid(false); + for (auto its_credential : p_it->get()->ids_) { + has_uid = has_gid = false; + for (auto its_range : std::get<0>(its_credential)) { + if (std::get<0>(its_range) <= _uid && _uid <= std::get<1>(its_range)) { + has_uid = true; + break; + } + } + for (auto its_range : std::get<1>(its_credential)) { + if (std::get<0>(its_range) <= _gid && _gid <= std::get<1>(its_range)) { + has_gid = true; + break; + } + } + // only remove "credentials allow" policies to prevent removal of + // blacklist configured in file + if (has_uid && has_gid && p_it->get()->allow_who_) { + was_removed = true; + break; + } + } + if (was_removed) { + p_it = any_client_policies_.erase(p_it); + break; + } else { + ++p_it; + } + } + } + return was_removed; +} + +void configuration_impl::update_security_policy(uint32_t _uid, uint32_t _gid, ::std::shared_ptr<policy> _policy) { + remove_security_policy(_uid, _gid); + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); + any_client_policies_.push_back(_policy); +} + +void configuration_impl::add_security_credentials(uint32_t _uid, uint32_t _gid, + ::std::shared_ptr<policy> _credentials_policy, client_t _client) { + + bool was_found(false); + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); + for (auto its_policy : any_client_policies_) { + bool has_uid(false), has_gid(false); + for (auto its_credential : its_policy->ids_) { + has_uid = has_gid = false; + for (auto its_range : std::get<0>(its_credential)) { + if (std::get<0>(its_range) <= _uid && _uid <= std::get<1>(its_range)) { + has_uid = true; + break; + } + } + for (auto its_range : std::get<1>(its_credential)) { + if (std::get<0>(its_range) <= _gid && _gid <= std::get<1>(its_range)) { + has_gid = true; + break; + } + } + if (has_uid && has_gid && its_policy->allow_who_) { + was_found = true; + break; + } + } + if (was_found) { + break; + } + } + // Do not add the new (credentials-only-policy) if a allow credentials policy with same credentials was found + if (!was_found) { + any_client_policies_.push_back(_credentials_policy); + VSOMEIP_INFO << __func__ << " Added security credentials at client: 0x" + << std::hex << _client << std::dec << " with UID: " << _uid << " GID: " << _gid; + } +} + +bool configuration_impl::is_remote_client_allowed() const { + if (!check_credentials_) { + return true; + } + return allow_remote_clients_; +} + +bool configuration_impl::is_policy_update_allowed(uint32_t _uid, std::shared_ptr<policy> &_policy) const { + bool uid_allowed(false); + { + std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); + for (auto its_uid_range : uid_whitelist_) { + if (std::get<0>(its_uid_range) <= _uid && _uid <= std::get<1>(its_uid_range)) { + uid_allowed = true; + break; + } + } + } + + if (uid_allowed) { + std::lock_guard<std::mutex> its_lock(service_interface_whitelist_mutex_); + for (auto its_request : _policy->services_) { + auto its_requested_service = std::get<0>(its_request); + bool has_service(false); + for (auto its_service_range : service_interface_whitelist_) { + if (std::get<0>(its_service_range) <= its_requested_service + && its_requested_service <= std::get<1>(its_service_range)) { + has_service = true; + break; + } + } + if (!has_service) { + if (!check_whitelist_) { + VSOMEIP_INFO << "vSomeIP Security: Policy update requesting service ID: " + << std::hex << its_requested_service + << " is not allowed, but will be allowed due to whitelist audit mode is active!"; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Policy update requesting service ID: " + << std::hex << its_requested_service + << " is not allowed! -> ignore update"; + } + return !check_whitelist_; + } + } + return true; + } else { + if (!check_whitelist_) { + VSOMEIP_INFO << "vSomeIP Security: Policy update for UID: " << std::dec << _uid + << " is not allowed, but will be allowed due to whitelist audit mode is active!"; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Policy update for UID: " << std::dec << _uid + << " is not allowed! -> ignore update"; + } + return !check_whitelist_; + } +} + +bool configuration_impl::is_policy_removal_allowed(uint32_t _uid) const { + std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); + for (auto its_uid_range : uid_whitelist_) { + if (std::get<0>(its_uid_range) <= _uid && _uid <= std::get<1>(its_uid_range)) { + return true; + } + } + + if (!check_whitelist_) { + VSOMEIP_INFO << "vSomeIP Security: Policy removal for UID: " << std::dec << _uid + << " is not allowed, but will be allowed due to whitelist audit mode is active!"; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Policy removal for UID: " << std::dec << _uid + << " is not allowed! -> ignore removal"; + } + return !check_whitelist_; +} + +bool configuration_impl::check_routing_credentials(client_t _client, uint32_t _uid, uint32_t _gid) const { + if (_client != get_id(routing_host_)) { + return true; + } else { + std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); + if ( std::get<0>(routing_credentials_) == _uid + && std::get<1>(routing_credentials_) == _gid) { + return true; + } + } + + std::string security_mode_text = "!"; + if (!check_routing_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" + << std::hex << _client << " and UID/GID=" << std::dec << _uid + << "/" << _gid << " : Check routing credentials failed as " + << "configured routing manager credentials " + << "do not match with routing manager credentials" + << security_mode_text; + + return !check_routing_credentials_; +} + +std::uint32_t configuration_impl::get_shutdown_timeout() const { + return shutdown_timeout_; +} + } // namespace config } // namespace vsomeip diff --git a/implementation/e2e_protection/include/e2exf/config.hpp b/implementation/e2e_protection/include/e2exf/config.hpp index 3667211..bf1fb50 100644 --- a/implementation/e2e_protection/include/e2exf/config.hpp +++ b/implementation/e2e_protection/include/e2exf/config.hpp @@ -6,6 +6,7 @@ #ifndef VSOMEIP_E2EXF_CONFIG_HPP #define VSOMEIP_E2EXF_CONFIG_HPP +#include <vsomeip/primitive_types.hpp> #include "../e2e/profile/profile_interface/checker.hpp" #include "../e2e/profile/profile_interface/protector.hpp" @@ -15,12 +16,9 @@ namespace vsomeip { namespace e2exf { -using session_id = uint16_t; -using instance_id = uint16_t; +using data_identifier_t = std::pair<service_t, event_t>; -using data_identifier = std::pair<session_id, instance_id>; - -std::ostream &operator<<(std::ostream &_os, const e2exf::data_identifier &_data_identifier); +std::ostream &operator<<(std::ostream &_os, const e2exf::data_identifier_t &_data_identifier); } // namespace e2exf } // namespace vsomeip diff --git a/implementation/e2e_protection/src/e2exf/config.cpp b/implementation/e2e_protection/src/e2exf/config.cpp index e210faf..c7df18a 100644 --- a/implementation/e2e_protection/src/e2exf/config.cpp +++ b/implementation/e2e_protection/src/e2exf/config.cpp @@ -8,7 +8,7 @@ namespace vsomeip { -std::ostream &operator<<(std::ostream &_os, const e2exf::data_identifier &_data_identifier) { +std::ostream &operator<<(std::ostream &_os, const e2exf::data_identifier_t &_data_identifier) { _os << _data_identifier.first << _data_identifier.second; return _os; } diff --git a/implementation/endpoints/include/client_endpoint_impl.hpp b/implementation/endpoints/include/client_endpoint_impl.hpp index 97819bc..409bba0 100644 --- a/implementation/endpoints/include/client_endpoint_impl.hpp +++ b/implementation/endpoints/include/client_endpoint_impl.hpp @@ -14,6 +14,7 @@ #include <boost/array.hpp>
#include <boost/asio/io_service.hpp>
+#include <boost/asio/strand.hpp> #include <boost/asio/ip/udp.hpp>
#include <boost/utility.hpp>
#include <vsomeip/constants.hpp>
@@ -53,7 +54,8 @@ public: bool is_client() const;
- bool is_connected() const;
+ bool is_established() const;
+ void set_established(bool _established);
void set_connected(bool _connected);
virtual bool get_remote_address(boost::asio::ip::address &_address) const;
virtual std::uint16_t get_remote_port() const;
@@ -76,6 +78,7 @@ protected: enum class cei_state_e : std::uint8_t {
CLOSED,
CONNECTING,
+ CONNECTED,
ESTABLISHED
};
virtual void send_queued() = 0;
@@ -98,6 +101,7 @@ protected: boost::asio::steady_timer connect_timer_;
std::atomic<uint32_t> connect_timeout_;
std::atomic<cei_state_e> state_;
+ std::atomic<std::uint32_t> reconnect_counter_;
// send data
message_buffer_ptr_t packetizer_;
@@ -110,9 +114,13 @@ protected: std::atomic<std::uint16_t> local_port_;
+ boost::asio::io_service::strand strand_; + private:
virtual void set_local_port() = 0;
virtual std::string get_remote_information() const = 0;
+ virtual std::uint32_t get_max_allowed_reconnects() const = 0;
+ virtual void max_allowed_reconnects_reached() = 0;
};
} // namespace vsomeip
diff --git a/implementation/endpoints/include/endpoint.hpp b/implementation/endpoints/include/endpoint.hpp index f3bf510..b1204f0 100644 --- a/implementation/endpoints/include/endpoint.hpp +++ b/implementation/endpoints/include/endpoint.hpp @@ -25,7 +25,7 @@ public: virtual void start() = 0;
virtual void stop() = 0;
- virtual bool is_connected() const = 0;
+ virtual bool is_established() const = 0;
virtual bool send(const byte_t *_data, uint32_t _size,
bool _flush = true) = 0;
@@ -57,7 +57,7 @@ public: virtual void print_status() = 0;
- virtual void set_connected(bool _connected) = 0;
+ virtual void set_established(bool _established) = 0;
virtual void set_connected(bool _connected) = 0;
};
} // namespace vsomeip
diff --git a/implementation/endpoints/include/local_client_endpoint_impl.hpp b/implementation/endpoints/include/local_client_endpoint_impl.hpp index 214a6d0..16b3af5 100644 --- a/implementation/endpoints/include/local_client_endpoint_impl.hpp +++ b/implementation/endpoints/include/local_client_endpoint_impl.hpp @@ -63,6 +63,8 @@ private: std::size_t _bytes); void set_local_port(); std::string get_remote_information() const; + std::uint32_t get_max_allowed_reconnects() const; + void max_allowed_reconnects_reached(); message_buffer_t recv_buffer_; }; diff --git a/implementation/endpoints/include/local_server_endpoint_impl.hpp b/implementation/endpoints/include/local_server_endpoint_impl.hpp index aee73c4..8ff8a10 100644 --- a/implementation/endpoints/include/local_server_endpoint_impl.hpp +++ b/implementation/endpoints/include/local_server_endpoint_impl.hpp @@ -43,7 +43,8 @@ public: boost::asio::io_service &_io, std::uint32_t _max_message_size, std::uint32_t _buffer_shrink_threshold, - configuration::endpoint_queue_limit_t _queue_limit); + configuration::endpoint_queue_limit_t _queue_limit, + std::uint32_t _mode); local_server_endpoint_impl(std::shared_ptr<endpoint_host> _host, endpoint_type _local, @@ -51,7 +52,8 @@ public: std::uint32_t _max_message_size, int native_socket, std::uint32_t _buffer_shrink_threshold, - configuration::endpoint_queue_limit_t _queue_limit); + configuration::endpoint_queue_limit_t _queue_limit, + std::uint32_t _mode); virtual ~local_server_endpoint_impl(); @@ -90,12 +92,14 @@ private: void send_queued(const queue_iterator_type _queue_iterator); void set_bound_client(client_t _client); + client_t get_bound_client() const; + std::size_t get_recv_buffer_capacity() const; private: connection(std::weak_ptr<local_server_endpoint_impl> _server, - std::uint32_t _recv_buffer_size_initial, std::uint32_t _max_message_size, + std::uint32_t _recv_buffer_size_initial, std::uint32_t _buffer_shrink_threshold, boost::asio::io_service &_io_service); @@ -133,6 +137,10 @@ private: typedef std::map<endpoint_type, connection::ptr> connections_t; std::mutex connections_mutex_; connections_t connections_; + + std::mutex client_connections_mutex_; + std::map<client_t, connection::ptr> client_connections_; + const std::uint32_t buffer_shrink_threshold_; private: diff --git a/implementation/endpoints/include/netlink_connector.hpp b/implementation/endpoints/include/netlink_connector.hpp index f71ba88..730874e 100644 --- a/implementation/endpoints/include/netlink_connector.hpp +++ b/implementation/endpoints/include/netlink_connector.hpp @@ -67,7 +67,7 @@ public: /// Get the underlying endpoint in the native type. data_type* data() { - return &sockaddr; + return reinterpret_cast<struct sockaddr*>(&sockaddr); } /// Get the underlying endpoint in the native type. diff --git a/implementation/endpoints/include/server_endpoint_impl.hpp b/implementation/endpoints/include/server_endpoint_impl.hpp index 60e8ff4..68e719b 100644 --- a/implementation/endpoints/include/server_endpoint_impl.hpp +++ b/implementation/endpoints/include/server_endpoint_impl.hpp @@ -38,7 +38,8 @@ public: bool is_client() const;
void restart(bool _force);
- bool is_connected() const;
+ bool is_established() const;
+ void set_established(bool _established);
void set_connected(bool _connected);
bool send(const uint8_t *_data, uint32_t _size, bool _flush);
bool send(const std::vector<byte_t>& _cmd_header, const byte_t *_data,
diff --git a/implementation/endpoints/include/tcp_client_endpoint_impl.hpp b/implementation/endpoints/include/tcp_client_endpoint_impl.hpp index cbadf5a..77cbd39 100644 --- a/implementation/endpoints/include/tcp_client_endpoint_impl.hpp +++ b/implementation/endpoints/include/tcp_client_endpoint_impl.hpp @@ -25,9 +25,11 @@ public: endpoint_type _remote,
boost::asio::io_service &_io,
std::uint32_t _max_message_size,
- std::uint32_t buffer_shrink_threshold,
+ std::uint32_t _buffer_shrink_threshold,
std::chrono::milliseconds _send_timeout,
- configuration::endpoint_queue_limit_t _queue_limit);
+ configuration::endpoint_queue_limit_t _queue_limit,
+ std::uint32_t _tcp_restart_aborts_max,
+ std::uint32_t _tcp_connect_time_max);
virtual ~tcp_client_endpoint_impl();
void start();
@@ -71,6 +73,8 @@ private: service_t _service, method_t _method, client_t _client, session_t _session,
std::chrono::steady_clock::time_point _start);
std::string get_remote_information() const;
+ std::uint32_t get_max_allowed_reconnects() const;
+ void max_allowed_reconnects_reached();
const std::uint32_t recv_buffer_size_initial_;
message_buffer_ptr_t recv_buffer_;
@@ -82,6 +86,11 @@ private: std::chrono::steady_clock::time_point last_cookie_sent_;
const std::chrono::milliseconds send_timeout_;
const std::chrono::milliseconds send_timeout_warning_;
+
+ std::uint32_t tcp_restart_aborts_max_;
+ std::uint32_t tcp_connect_time_max_;
+ std::atomic<uint32_t> aborted_restart_count_;
+ std::chrono::steady_clock::time_point connect_timepoint_;
};
} // namespace vsomeip
diff --git a/implementation/endpoints/include/udp_client_endpoint_impl.hpp b/implementation/endpoints/include/udp_client_endpoint_impl.hpp index 1866d05..b11da93 100644 --- a/implementation/endpoints/include/udp_client_endpoint_impl.hpp +++ b/implementation/endpoints/include/udp_client_endpoint_impl.hpp @@ -30,14 +30,15 @@ public: endpoint_type _local,
endpoint_type _remote,
boost::asio::io_service &_io,
- configuration::endpoint_queue_limit_t _queue_limit);
+ configuration::endpoint_queue_limit_t _queue_limit,
+ std::uint32_t _udp_receive_buffer_size);
virtual ~udp_client_endpoint_impl();
void start();
void restart(bool _force);
void receive_cbk(boost::system::error_code const &_error,
- std::size_t _bytes);
+ std::size_t _bytes, message_buffer_ptr_t _recv_buffer);
bool get_remote_address(boost::asio::ip::address &_address) const;
std::uint16_t get_remote_port() const;
@@ -51,11 +52,12 @@ private: const std::string get_address_port_remote() const;
const std::string get_address_port_local() const;
std::string get_remote_information() const;
-
- message_buffer_t recv_buffer_;
+ std::uint32_t get_max_allowed_reconnects() const;
+ void max_allowed_reconnects_reached();
const boost::asio::ip::address remote_address_;
const std::uint16_t remote_port_;
+ const std::uint32_t udp_receive_buffer_size_;
};
} // namespace vsomeip
diff --git a/implementation/endpoints/include/udp_server_endpoint_impl.hpp b/implementation/endpoints/include/udp_server_endpoint_impl.hpp index 48e4935..f1b6959 100644 --- a/implementation/endpoints/include/udp_server_endpoint_impl.hpp +++ b/implementation/endpoints/include/udp_server_endpoint_impl.hpp @@ -26,7 +26,8 @@ public: udp_server_endpoint_impl(std::shared_ptr<endpoint_host> _host,
endpoint_type _local,
boost::asio::io_service &_io,
- configuration::endpoint_queue_limit_t _queue_limit);
+ configuration::endpoint_queue_limit_t _queue_limit,
+ std::uint32_t _udp_receive_buffer_size);
virtual ~udp_server_endpoint_impl();
void start();
@@ -64,6 +65,8 @@ private: std::string get_remote_information(
const queue_iterator_type _queue_iterator) const;
+ const std::string get_address_port_local() const;
+
private:
socket_type socket_;
endpoint_type remote_;
@@ -75,7 +78,7 @@ private: std::atomic<bool> joined_group_;
message_buffer_t recv_buffer_;
- std::mutex socket_mutex_;
+ mutable std::mutex socket_mutex_;
const std::uint16_t local_port_;
};
diff --git a/implementation/endpoints/include/virtual_server_endpoint_impl.hpp b/implementation/endpoints/include/virtual_server_endpoint_impl.hpp index 58619cc..afe56e2 100644 --- a/implementation/endpoints/include/virtual_server_endpoint_impl.hpp +++ b/implementation/endpoints/include/virtual_server_endpoint_impl.hpp @@ -24,7 +24,8 @@ public: void start(); void stop(); - bool is_connected() const; + bool is_established() const; + void set_established(bool _established); void set_connected(bool _connected); bool send(const byte_t *_data, uint32_t _size, bool _flush); diff --git a/implementation/endpoints/src/client_endpoint_impl.cpp b/implementation/endpoints/src/client_endpoint_impl.cpp index b4a6bf9..2f521ea 100644 --- a/implementation/endpoints/src/client_endpoint_impl.cpp +++ b/implementation/endpoints/src/client_endpoint_impl.cpp @@ -38,10 +38,12 @@ client_endpoint_impl<Protocol>::client_endpoint_impl( flush_timer_(_io), connect_timer_(_io),
connect_timeout_(VSOMEIP_DEFAULT_CONNECT_TIMEOUT), // TODO: use config variable
state_(cei_state_e::CLOSED),
+ reconnect_counter_(0),
packetizer_(std::make_shared<message_buffer_t>()),
queue_size_(0),
was_not_connected_(false),
- local_port_(0) {
+ local_port_(0), + strand_(_io) { }
template<typename Protocol>
@@ -54,22 +56,38 @@ bool client_endpoint_impl<Protocol>::is_client() const { }
template<typename Protocol>
-bool client_endpoint_impl<Protocol>::is_connected() const {
+bool client_endpoint_impl<Protocol>::is_established() const {
return state_ == cei_state_e::ESTABLISHED;
}
template<typename Protocol>
+void client_endpoint_impl<Protocol>::set_established(bool _established) {
+ if (_established) {
+ if (state_ != cei_state_e::CONNECTING) {
+ std::lock_guard<std::mutex> its_lock(socket_mutex_);
+ if (socket_->is_open()) {
+ state_ = cei_state_e::ESTABLISHED;
+ } else {
+ state_ = cei_state_e::CLOSED;
+ }
+ }
+ } else {
+ state_ = cei_state_e::CLOSED;
+ }
}
+template<typename Protocol>
void client_endpoint_impl<Protocol>::set_connected(bool _connected) {
if (_connected) {
std::lock_guard<std::mutex> its_lock(socket_mutex_);
if (socket_->is_open()) {
- state_ = cei_state_e::ESTABLISHED;
+ state_ = cei_state_e::CONNECTED;
} else {
state_ = cei_state_e::CLOSED;
}
} else {
state_ = cei_state_e::CLOSED;
- }
}
+ }
+}
+
template<typename Protocol>
void client_endpoint_impl<Protocol>::stop() {
{
std::lock_guard<std::mutex> its_lock(mutex_);
@@ -159,7 +177,8 @@ bool client_endpoint_impl<Protocol>::flush() { template<typename Protocol>
void client_endpoint_impl<Protocol>::connect_cbk(
boost::system::error_code const &_error) {
- if (_error == boost::asio::error::operation_aborted) {
+ if (_error == boost::asio::error::operation_aborted
+ || endpoint_impl<Protocol>::sending_blocked_) {
// endpoint was stopped
shutdown_and_close_socket(false);
return;
@@ -173,7 +192,12 @@ void client_endpoint_impl<Protocol>::connect_cbk( state_ = cei_state_e::CLOSED;
its_host->on_disconnect(this->shared_from_this());
}
- start_connect_timer();
+ if (get_max_allowed_reconnects() == MAX_RECONNECTS_UNLIMITED ||
+ get_max_allowed_reconnects() >= ++reconnect_counter_) {
+ start_connect_timer();
+ } else {
+ max_allowed_reconnects_reached();
+ }
// Double the timeout as long as the maximum allowed is larger
if (connect_timeout_ < VSOMEIP_MAX_CONNECT_TIMEOUT)
connect_timeout_ = (connect_timeout_ << 1);
@@ -183,13 +207,8 @@ void client_endpoint_impl<Protocol>::connect_cbk( connect_timer_.cancel();
}
connect_timeout_ = VSOMEIP_DEFAULT_CONNECT_TIMEOUT; // TODO: use config variable
+ reconnect_counter_ = 0;
set_local_port();
- if (state_ != cei_state_e::ESTABLISHED) {
- its_host->on_connect(this->shared_from_this());
- }
-
- receive();
-
if (was_not_connected_) {
was_not_connected_ = false;
std::lock_guard<std::mutex> its_lock(mutex_);
@@ -199,6 +218,10 @@ void client_endpoint_impl<Protocol>::connect_cbk( << get_remote_information();
}
}
+ if (state_ != cei_state_e::ESTABLISHED) {
+ its_host->on_connect(this->shared_from_this());
+ }
+ receive();
}
}
}
@@ -269,8 +292,17 @@ void client_endpoint_impl<Protocol>::send_cbk( shutdown_and_close_socket(true);
connect();
} else if (_error == boost::asio::error::not_connected
- || _error == boost::asio::error::bad_descriptor) {
+ || _error == boost::asio::error::bad_descriptor
+ || _error == boost::asio::error::no_permission) {
state_ = cei_state_e::CLOSED;
+ if (_error == boost::asio::error::no_permission) {
+ VSOMEIP_WARNING << "cei::send_cbk received error: " << _error.message()
+ << " (" << std::dec << _error.value() << ") "
+ << get_remote_information();
+ std::lock_guard<std::mutex> its_lock(mutex_);
+ queue_.clear();
+ queue_size_ = 0;
+ }
was_not_connected_ = true;
shutdown_and_close_socket(true);
connect();
@@ -332,6 +364,12 @@ template<typename Protocol> void client_endpoint_impl<Protocol>::shutdown_and_close_socket_unlocked(bool _recreate_socket) {
local_port_ = 0;
if (socket_->is_open()) {
+#ifndef _WIN32 + if (-1 == fcntl(socket_->native_handle(), F_GETFD)) { + VSOMEIP_ERROR << "cei::shutdown_and_close_socket_unlocked: socket/handle closed already '" << std::string(std::strerror(errno)) + << "' (" << errno << ") " << get_remote_information(); + } +#endif boost::system::error_code its_error;
socket_->shutdown(Protocol::socket::shutdown_both, its_error);
socket_->close(its_error);
diff --git a/implementation/endpoints/src/credentials.cpp b/implementation/endpoints/src/credentials.cpp index 84f7f08..411fd3f 100644 --- a/implementation/endpoints/src/credentials.cpp +++ b/implementation/endpoints/src/credentials.cpp @@ -17,14 +17,16 @@ namespace vsomeip { void credentials::activate_credentials(const int _fd) { int optval = 1; if (setsockopt(_fd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { - VSOMEIP_ERROR << "Activating socket option for receiving credentials failed."; + VSOMEIP_ERROR << "vSomeIP Security: Activating socket option for receiving " + << "credentials failed."; } } void credentials::deactivate_credentials(const int _fd) { int optval = 0; if (setsockopt(_fd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { - VSOMEIP_ERROR << "Deactivating socket option for receiving credentials failed."; + VSOMEIP_ERROR << "vSomeIP Security: Deactivating socket option for receiving " + << "credentials failed."; } } @@ -62,13 +64,13 @@ client_t credentials::receive_credentials(const int _fd, uid_t& _uid, gid_t& _gi // Receive client_id plus ancillary data ssize_t nr = recvmsg(_fd, &msgh, 0); if (nr == -1) { - VSOMEIP_ERROR << "Receiving credentials failed. No data."; + VSOMEIP_ERROR << "vSomeIP Security: Receiving credentials failed. No data."; } cmhp = CMSG_FIRSTHDR(&msgh); if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred)) || cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) { - VSOMEIP_ERROR << "Receiving credentials failed. Invalid data."; + VSOMEIP_ERROR << "vSomeIP Security: Receiving credentials failed. Invalid data."; } else { ucredp = (struct ucred *) CMSG_DATA(cmhp); _uid = ucredp->uid; diff --git a/implementation/endpoints/src/local_client_endpoint_impl.cpp b/implementation/endpoints/src/local_client_endpoint_impl.cpp index 29d861f..a57c831 100644 --- a/implementation/endpoints/src/local_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/local_client_endpoint_impl.cpp @@ -60,6 +60,8 @@ void local_client_endpoint_impl::restart(bool _force) { std::lock_guard<std::mutex> its_lock(socket_mutex_);
shutdown_and_close_socket_unlocked(true);
}
+ was_not_connected_ = true;
+ reconnect_counter_ = 0;
start_connect_timer();
}
@@ -139,13 +141,14 @@ void local_client_endpoint_impl::connect() { } else {
VSOMEIP_WARNING << "local_client_endpoint::connect: Error opening socket: "
- << its_error.message();
- return;
+ << its_error.message() << " (" << std::dec << its_error.value()
+ << ")";
+ its_connect_error = its_error;
}
}
// call connect_cbk asynchronously
try {
- service_.post(
+ strand_.post( std::bind(&client_endpoint_impl::connect_cbk, shared_from_this(),
its_connect_error));
} catch (const std::exception &e) {
@@ -158,13 +161,15 @@ void local_client_endpoint_impl::receive() { if (socket_->is_open()) {
socket_->async_receive(
boost::asio::buffer(recv_buffer_),
- std::bind(
- &local_client_endpoint_impl::receive_cbk,
- std::dynamic_pointer_cast<
- local_client_endpoint_impl
- >(shared_from_this()),
- std::placeholders::_1,
- std::placeholders::_2
+ strand_.wrap( + std::bind( + &local_client_endpoint_impl::receive_cbk, + std::dynamic_pointer_cast< + local_client_endpoint_impl + >(shared_from_this()), + std::placeholders::_1, + std::placeholders::_2 + ) )
);
}
@@ -232,10 +237,6 @@ void local_client_endpoint_impl::receive_cbk( VSOMEIP_ERROR << "Local endpoint received message ("
<< _error.message() << ")";
}
- // The error handler is set only if the endpoint is hosted by the
- // routing manager. For the routing manager proxies, the corresponding
- // client endpoint (that connect to the same client) are removed
- // after the proxy has received the routing info.
error_handler_t handler;
{
std::lock_guard<std::mutex> its_lock(error_handler_mutex_);
@@ -290,6 +291,10 @@ std::string local_client_endpoint_impl::get_remote_information() const { #endif
}
+std::uint32_t local_client_endpoint_impl::get_max_allowed_reconnects() const {
+ return 13;
+}
+
bool local_client_endpoint_impl::send(const std::vector<byte_t>& _cmd_header,
const byte_t *_data, uint32_t _size,
bool _flush) {
@@ -319,4 +324,16 @@ bool local_client_endpoint_impl::send(const std::vector<byte_t>& _cmd_header, return ret;
}
+void local_client_endpoint_impl::max_allowed_reconnects_reached() {
+ VSOMEIP_ERROR << "local_client_endpoint::max_allowed_reconnects_reached: "
+ << get_remote_information();
+ error_handler_t handler;
+ {
+ std::lock_guard<std::mutex> its_lock(error_handler_mutex_);
+ handler = error_handler_;
+ }
+ if (handler)
+ handler();
+}
+
} // namespace vsomeip
diff --git a/implementation/endpoints/src/local_server_endpoint_impl.cpp b/implementation/endpoints/src/local_server_endpoint_impl.cpp index 6ae9c6c..cfd5c0b 100644 --- a/implementation/endpoints/src/local_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/local_server_endpoint_impl.cpp @@ -30,7 +30,7 @@ local_server_endpoint_impl::local_server_endpoint_impl( endpoint_type _local, boost::asio::io_service &_io, std::uint32_t _max_message_size, std::uint32_t _buffer_shrink_threshold, - configuration::endpoint_queue_limit_t _queue_limit) + configuration::endpoint_queue_limit_t _queue_limit, std::uint32_t _mode) : local_server_endpoint_base_impl(_host, _local, _io, _max_message_size, _queue_limit), acceptor_(_io), @@ -48,8 +48,11 @@ local_server_endpoint_impl::local_server_endpoint_impl( boost::asio::detail::throw_error(ec, "acceptor listen"); #ifndef _WIN32 + if (chmod(_local.path().c_str(), static_cast<mode_t>(_mode)) == -1) { + VSOMEIP_ERROR << __func__ << ": chmod: " << strerror(errno); + } if (_host->get_configuration()->is_security_enabled()) { - credentials::activate_credentials(acceptor_.native()); + credentials::activate_credentials(acceptor_.native_handle()); } #endif } @@ -60,7 +63,7 @@ local_server_endpoint_impl::local_server_endpoint_impl( std::uint32_t _max_message_size, int native_socket, std::uint32_t _buffer_shrink_threshold, - configuration::endpoint_queue_limit_t _queue_limit) + configuration::endpoint_queue_limit_t _queue_limit, std::uint32_t _mode) : local_server_endpoint_base_impl(_host, _local, _io, _max_message_size, _queue_limit), acceptor_(_io), @@ -72,8 +75,11 @@ local_server_endpoint_impl::local_server_endpoint_impl( boost::asio::detail::throw_error(ec, "acceptor assign native socket"); #ifndef _WIN32 + if (chmod(_local.path().c_str(), static_cast<mode_t>(_mode)) == -1) { + VSOMEIP_ERROR << __func__ << ": chmod: " << strerror(errno); + } if (_host->get_configuration()->is_security_enabled()) { - credentials::activate_credentials(acceptor_.native()); + credentials::activate_credentials(acceptor_.native_handle()); } #endif } @@ -127,6 +133,12 @@ void local_server_endpoint_impl::stop() { } connections_.clear(); } +#ifndef _WIN32 + { + std::lock_guard<std::mutex> its_lock(client_connections_mutex_); + client_connections_.clear(); + } +#endif } bool local_server_endpoint_impl::send_to( @@ -178,15 +190,31 @@ bool local_server_endpoint_impl::get_default_target( void local_server_endpoint_impl::remove_connection( local_server_endpoint_impl::connection *_connection) { - std::lock_guard<std::mutex> its_lock(connections_mutex_); - for (auto it = connections_.begin(); it != connections_.end();) { - if (it->second.get() == _connection) { - it = connections_.erase(it); - break; - } else { - ++it; + { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + for (auto it = connections_.begin(); it != connections_.end();) { + if (it->second.get() == _connection) { + it = connections_.erase(it); + break; + } else { + ++it; + } } } + +#ifndef _WIN32 + { + std::lock_guard<std::mutex> its_lock(client_connections_mutex_); + for (auto it = client_connections_.begin(); it != client_connections_.end();) { + if (it->second.get() == _connection) { + it = client_connections_.erase(it); + break; + } else { + ++it; + } + } + } +#endif } void local_server_endpoint_impl::accept_cbk( @@ -196,23 +224,62 @@ void local_server_endpoint_impl::accept_cbk( && _error != boost::asio::error::operation_aborted && _error != boost::asio::error::no_descriptors) { start(); + } else if (_error == boost::asio::error::no_descriptors) { + VSOMEIP_ERROR << "local_server_endpoint_impl::accept_cbk: " + << _error.message() << " (" << std::dec << _error.value() + << ") Will try to accept again in 1000ms"; + std::shared_ptr<boost::asio::steady_timer> its_timer = + std::make_shared<boost::asio::steady_timer>(service_, + std::chrono::milliseconds(1000)); + auto its_ep = std::dynamic_pointer_cast<local_server_endpoint_impl>( + shared_from_this()); + its_timer->async_wait([its_timer, its_ep] + (const boost::system::error_code& _error) { + if (!_error) { + its_ep->start(); + } + }); } if (!_error) { #ifndef _WIN32 auto its_host = host_.lock(); + client_t client = 0; if (its_host) { if (its_host->get_configuration()->is_security_enabled()) { std::unique_lock<std::mutex> its_socket_lock(_connection->get_socket_lock()); socket_type &new_connection_socket = _connection->get_socket(); - uid_t uid(0); - gid_t gid(0); - client_t client = credentials::receive_credentials( + uid_t uid(0xffffffff); + gid_t gid(0xffffffff); + client = credentials::receive_credentials( new_connection_socket.native(), uid, gid); + + std::lock_guard<std::mutex> its_client_connection_lock(client_connections_mutex_); + auto found_client = client_connections_.find(client); + if (found_client != client_connections_.end()) { + VSOMEIP_WARNING << std::hex << "vSomeIP Security: Rejecting new connection with client ID 0x" << client + << " uid/gid= " << std::dec << uid << "/" << gid + << " because of already existing connection using same client ID"; + boost::system::error_code er; + new_connection_socket.shutdown(new_connection_socket.shutdown_both, er); + new_connection_socket.close(er); + return; + } + + if (!its_host->get_configuration()->check_routing_credentials(client, uid, gid)) { + VSOMEIP_WARNING << std::hex << "vSomeIP Security: Rejecting new connection with routing manager client ID 0x" << client + << " uid/gid= " << std::dec << uid << "/" << gid + << " because passed credentials do not match with routing manager credentials!"; + boost::system::error_code er; + new_connection_socket.shutdown(new_connection_socket.shutdown_both, er); + new_connection_socket.close(er); + return; + } + if (!its_host->check_credentials(client, uid, gid)) { - VSOMEIP_WARNING << std::hex << "Client 0x" << its_host->get_client() - << " received client credentials from client 0x" << client - << " which violates the security policy : uid/gid=" + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex + << its_host->get_client() << " received client credentials from client 0x" + << client << " which violates the security policy : uid/gid=" << std::dec << uid << "/" << gid; boost::system::error_code er; new_connection_socket.shutdown(new_connection_socket.shutdown_both, er); @@ -234,8 +301,16 @@ void local_server_endpoint_impl::accept_cbk( } if (!its_error) { { - std::lock_guard<std::mutex> its_lock(connections_mutex_); - connections_[remote] = _connection; + { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + connections_[remote] = _connection; + } +#ifndef _WIN32 + { + std::lock_guard<std::mutex> its_lock(client_connections_mutex_); + client_connections_[client] = _connection; + } +#endif } _connection->start(); } @@ -271,7 +346,6 @@ local_server_endpoint_impl::connection::create( std::uint32_t _buffer_shrink_threshold, boost::asio::io_service &_io_service) { const std::uint32_t its_initial_buffer_size = VSOMEIP_COMMAND_HEADER_SIZE - + VSOMEIP_MAX_LOCAL_MESSAGE_SIZE + static_cast<std::uint32_t>(sizeof(instance_t) + sizeof(bool) + sizeof(bool)); return ptr(new connection(_server, _max_message_size, its_initial_buffer_size, @@ -334,6 +408,12 @@ void local_server_endpoint_impl::connection::start() { void local_server_endpoint_impl::connection::stop() { std::lock_guard<std::mutex> its_lock(socket_mutex_); if (socket_.is_open()) { +#ifndef _WIN32 + if (-1 == fcntl(socket_.native_handle(), F_GETFD)) { + VSOMEIP_ERROR << "lse: socket/handle closed already '" << std::string(std::strerror(errno)) + << "' (" << errno << ") " << get_path_local(); + } +#endif boost::system::error_code its_error; socket_.shutdown(socket_.shutdown_both, its_error); socket_.close(its_error); @@ -487,19 +567,33 @@ void local_server_endpoint_impl::connection::receive_cbk( // start tag (4 Byte) + command (1 Byte) + client id (2 Byte) // + command size (4 Byte) + data itself + stop tag (4 byte) // = 15 Bytes not covered in command size. - if (its_command_size && its_command_size + 15 > recv_buffer_size_) { + if (its_command_size + 15 > recv_buffer_size_) { missing_capacity_ = its_command_size + 15 - std::uint32_t(recv_buffer_size_); } else if (recv_buffer_size_ < 11) { // to little data to read out the command size // minimal amount of data needed to read out command size = 11 missing_capacity_ = 11 - static_cast<std::uint32_t>(recv_buffer_size_); } else { + std::stringstream local_msg; + for (std::size_t i = its_iteration_gap; + i < recv_buffer_size_ + its_iteration_gap && + i - its_iteration_gap < 32; i++) { + local_msg << std::setw(2) << std::setfill('0') + << std::hex << (int) recv_buffer_[i] << " "; + } VSOMEIP_ERROR << "lse::c<" << this << ">rcb: recv_buffer_size is: " << std::dec << recv_buffer_size_ << " but couldn't read " "out command size. recv_buffer_capacity: " - << recv_buffer_.capacity() - << " its_iteration_gap: " << its_iteration_gap; + << std::dec << recv_buffer_.capacity() + << " its_iteration_gap: " << std::dec + << its_iteration_gap << " bound client: 0x" + << std::hex << bound_client_ << " buffer: " + << local_msg.str(); + recv_buffer_size_ = 0; + missing_capacity_ = 0; + its_iteration_gap = 0; + message_is_empty = true; } } } @@ -525,7 +619,7 @@ void local_server_endpoint_impl::connection::receive_cbk( found_message = true; its_iteration_gap = its_end + 4; } else { - if (!message_is_empty && its_iteration_gap) { + if (its_iteration_gap) { // Message not complete and not in front of the buffer! // Copy last part to front for consume in future receive_cbk call! for (size_t i = 0; i < recv_buffer_size_; ++i) { @@ -545,6 +639,7 @@ void local_server_endpoint_impl::connection::receive_cbk( || _error == boost::asio::error::connection_reset) { stop(); its_server->remove_connection(this); + its_host->get_configuration()->remove_client_to_uid_gid_mapping(bound_client_); } else if (_error != boost::asio::error::bad_descriptor) { start(); } @@ -555,6 +650,11 @@ void local_server_endpoint_impl::connection::set_bound_client(client_t _client) bound_client_ = _client; } +client_t local_server_endpoint_impl::connection::get_bound_client() const { + return bound_client_; +} + + void local_server_endpoint_impl::connection::calculate_shrink_count() { if (buffer_shrink_threshold_) { if (recv_buffer_.capacity() != recv_buffer_size_initial_) { @@ -624,6 +724,12 @@ void local_server_endpoint_impl::connection::handle_recv_buffer_exception( VSOMEIP_ERROR << its_message.str(); recv_buffer_.clear(); if (socket_.is_open()) { +#ifndef _WIN32 + if (-1 == fcntl(socket_.native_handle(), F_GETFD)) { + VSOMEIP_ERROR << "lse: socket/handle closed already '" << std::string(std::strerror(errno)) + << "' (" << errno << ") " << get_path_local(); + } +#endif boost::system::error_code its_error; socket_.shutdown(socket_.shutdown_both, its_error); socket_.close(its_error); diff --git a/implementation/endpoints/src/netlink_connector.cpp b/implementation/endpoints/src/netlink_connector.cpp index 94b75cf..2f9e2e5 100644 --- a/implementation/endpoints/src/netlink_connector.cpp +++ b/implementation/endpoints/src/netlink_connector.cpp @@ -103,7 +103,7 @@ void netlink_connector::receive_cbk(boost::system::error_code const &_error, struct nlmsghdr *nlh = (struct nlmsghdr *)&recv_buffer_[0]; while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) { - char ifname[1024]; + char ifname[IF_NAMESIZE]; switch (nlh->nlmsg_type) { case RTM_NEWADDR: { // New Address information @@ -356,7 +356,7 @@ bool netlink_connector::check_sd_multicast_route_match(struct rtmsg* _routemsg, struct rtattr *retrta; retrta = static_cast<struct rtattr *>(RTM_RTA(_routemsg)); int if_index(0); - char if_name[1024] = "n/a"; + char if_name[IF_NAMESIZE] = "n/a"; char address[INET6_ADDRSTRLEN] = "n/a"; char gateway[INET6_ADDRSTRLEN] = "n/a"; bool matches_sd_multicast(false); diff --git a/implementation/endpoints/src/server_endpoint_impl.cpp b/implementation/endpoints/src/server_endpoint_impl.cpp index 6da5826..915bfc9 100644 --- a/implementation/endpoints/src/server_endpoint_impl.cpp +++ b/implementation/endpoints/src/server_endpoint_impl.cpp @@ -19,6 +19,7 @@ #include "../../logging/include/logger.hpp"
#include "../../utility/include/byteorder.hpp"
#include "../../utility/include/utility.hpp"
+#include "../../service_discovery/include/defines.hpp"
namespace vsomeip {
@@ -54,12 +55,17 @@ void server_endpoint_impl<Protocol>::restart(bool _force) { }
template<typename Protocol>
-bool server_endpoint_impl<Protocol>::is_connected() const {
+bool server_endpoint_impl<Protocol>::is_established() const {
return true;
}
template<typename Protocol>
-void server_endpoint_impl<Protocol>::set_connected(bool _connected) {
(void) _connected;
}
+void server_endpoint_impl<Protocol>::set_established(bool _established) {
(void) _established;
}
+
+template<typename Protocol>
+void server_endpoint_impl<Protocol>::set_connected(bool _connected) {
+ (void) _connected;
+}
template<typename Protocol>
bool server_endpoint_impl<Protocol>::send(const uint8_t *_data,
uint32_t _size, bool _flush) {
#if 0
@@ -102,6 +108,16 @@ template<typename Protocol>
bool server_endpoint_impl<Protocol>::send(const uint VSOMEIP_WARNING << "server_endpoint::send: session_id 0x"
<< std::hex << its_session
<< " not found for client 0x" << its_client;
+ const method_t its_method =
+ VSOMEIP_BYTES_TO_WORD(_data[VSOMEIP_METHOD_POS_MIN],
+ _data[VSOMEIP_METHOD_POS_MAX]);
+ if (its_service == VSOMEIP_SD_SERVICE
+ && its_method == VSOMEIP_SD_METHOD) {
+ VSOMEIP_ERROR << "Clearing clients map as a request was "
+ "received on SD port";
+ clients_.clear();
+ is_valid_target = get_default_target(its_service, its_target);
+ }
}
} else {
is_valid_target = get_default_target(its_service, its_target);
diff --git a/implementation/endpoints/src/tcp_client_endpoint_impl.cpp b/implementation/endpoints/src/tcp_client_endpoint_impl.cpp index cb92619..41254f6 100644 --- a/implementation/endpoints/src/tcp_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/tcp_client_endpoint_impl.cpp @@ -30,7 +30,9 @@ tcp_client_endpoint_impl::tcp_client_endpoint_impl( std::uint32_t _max_message_size,
std::uint32_t _buffer_shrink_threshold,
std::chrono::milliseconds _send_timeout,
- configuration::endpoint_queue_limit_t _queue_limit)
+ configuration::endpoint_queue_limit_t _queue_limit,
+ std::uint32_t _tcp_restart_aborts_max,
+ std::uint32_t _tcp_connect_time_max)
: tcp_client_endpoint_base_impl(_host, _local, _remote, _io,
_max_message_size, _queue_limit),
recv_buffer_size_initial_(VSOMEIP_SOMEIP_HEADER_SIZE),
@@ -41,7 +43,10 @@ tcp_client_endpoint_impl::tcp_client_endpoint_impl( remote_port_(_remote.port()),
last_cookie_sent_(std::chrono::steady_clock::now() - std::chrono::seconds(11)),
send_timeout_(_send_timeout),
- send_timeout_warning_(_send_timeout / 2) {
+ send_timeout_warning_(_send_timeout / 2),
+ tcp_restart_aborts_max_(_tcp_restart_aborts_max),
+ tcp_connect_time_max_(_tcp_connect_time_max),
+ aborted_restart_count_(0) {
is_supporting_magic_cookies_ = true;
}
@@ -62,7 +67,19 @@ void tcp_client_endpoint_impl::start() { void tcp_client_endpoint_impl::restart(bool _force) {
if (!_force && state_ == cei_state_e::CONNECTING) {
- return;
+ std::chrono::steady_clock::time_point its_current
+ = std::chrono::steady_clock::now();
+ long its_connect_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+ its_current - connect_timepoint_).count();
+ if (aborted_restart_count_ < tcp_restart_aborts_max_
+ && its_connect_duration < tcp_connect_time_max_) {
+ aborted_restart_count_++;
+ return;
+ } else {
+ VSOMEIP_WARNING << "tce::restart: maximum number of aborted restarts ["
+ << tcp_restart_aborts_max_ << "] reached! its_connect_duration: "
+ << its_connect_duration;
+ }
}
state_ = cei_state_e::CONNECTING;
std::string address_port_local;
@@ -72,6 +89,8 @@ void tcp_client_endpoint_impl::restart(bool _force) { shutdown_and_close_socket_unlocked(true);
recv_buffer_ = std::make_shared<message_buffer_t>(recv_buffer_size_initial_, 0);
}
+ was_not_connected_ = true;
+ reconnect_counter_ = 0;
{
std::lock_guard<std::mutex> its_lock(mutex_);
for (const auto&m : queue_) {
@@ -150,7 +169,7 @@ void tcp_client_endpoint_impl::connect() { << " remote:" << get_address_port_remote();
try {
// don't connect on bind error to avoid using a random port
- service_.post(std::bind(&client_endpoint_impl::connect_cbk,
+ strand_.post(std::bind(&client_endpoint_impl::connect_cbk, shared_from_this(), its_bind_error));
} catch (const std::exception &e) {
VSOMEIP_ERROR << "tcp_client_endpoint_impl::connect: "
@@ -160,17 +179,23 @@ void tcp_client_endpoint_impl::connect() { }
}
state_ = cei_state_e::CONNECTING;
+ connect_timepoint_ = std::chrono::steady_clock::now();
+ aborted_restart_count_ = 0;
socket_->async_connect(
remote_,
- std::bind(
- &tcp_client_endpoint_base_impl::connect_cbk,
- shared_from_this(),
- std::placeholders::_1
+ strand_.wrap( + std::bind( + &tcp_client_endpoint_base_impl::connect_cbk, + shared_from_this(), + std::placeholders::_1 + ) )
);
} else {
VSOMEIP_WARNING << "tcp_client_endpoint::connect: Error opening socket: "
<< its_error.message() << " remote:" << get_address_port_remote();
+ strand_.post(std::bind(&tcp_client_endpoint_base_impl::connect_cbk,
+ shared_from_this(), its_error));
}
}
@@ -200,6 +225,12 @@ void tcp_client_endpoint_impl::receive(message_buffer_ptr_t _recv_buffer, if (its_capacity < its_required_capacity) {
_recv_buffer->reserve(its_required_capacity);
_recv_buffer->resize(its_required_capacity, 0x0);
+ if (_recv_buffer->size() > 1048576) {
+ VSOMEIP_INFO << "tce: recv_buffer size is: " <<
+ _recv_buffer->size()
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ }
}
buffer_size = _missing_capacity;
} else if (buffer_shrink_threshold_
@@ -217,13 +248,15 @@ void tcp_client_endpoint_impl::receive(message_buffer_ptr_t _recv_buffer, }
socket_->async_receive(
boost::asio::buffer(&(*_recv_buffer)[_recv_buffer_size], buffer_size),
- std::bind(
- &tcp_client_endpoint_impl::receive_cbk,
- std::dynamic_pointer_cast< tcp_client_endpoint_impl >(shared_from_this()),
- std::placeholders::_1,
- std::placeholders::_2,
- _recv_buffer,
- _recv_buffer_size
+ strand_.wrap( + std::bind( + &tcp_client_endpoint_impl::receive_cbk, + std::dynamic_pointer_cast< tcp_client_endpoint_impl >(shared_from_this()), + std::placeholders::_1, + std::placeholders::_2, + _recv_buffer, + _recv_buffer_size + ) )
);
}
@@ -431,7 +464,7 @@ void tcp_client_endpoint_impl::receive_cbk( return;
}
uint32_t current_message_size = static_cast<uint32_t>(read_message_size);
- has_full_message = (current_message_size > VSOMEIP_SOMEIP_HEADER_SIZE
+ has_full_message = (current_message_size > VSOMEIP_RETURN_CODE_POS
&& current_message_size <= _recv_buffer_size);
if (has_full_message) {
bool needs_forwarding(true);
@@ -472,58 +505,125 @@ void tcp_client_endpoint_impl::receive_cbk( _recv_buffer_size -= current_message_size;
its_iteration_gap += current_message_size;
its_missing_capacity = 0;
- } else if (max_message_size_ != MESSAGE_SIZE_UNLIMITED &&
- current_message_size > max_message_size_) {
- _recv_buffer_size = 0;
- _recv_buffer->resize(recv_buffer_size_initial_, 0x0);
- _recv_buffer->shrink_to_fit();
- if (has_enabled_magic_cookies_) {
- VSOMEIP_ERROR << "Received a TCP message which exceeds "
- << "maximum message size ("
- << std::dec << current_message_size
- << "). Magic Cookies are enabled: "
- << "Resetting receiver. local: "
- << get_address_port_local() << " remote: "
- << get_address_port_remote();
- } else {
- VSOMEIP_ERROR << "Received a TCP message which exceeds "
- << "maximum message size ("
- << std::dec << current_message_size
- << ") Magic cookies are disabled: "
- << "Client will be disabled! local: "
- << get_address_port_local() << " remote: "
- << get_address_port_remote();
- return;
- }
- } else if (current_message_size > _recv_buffer_size) {
- its_missing_capacity = current_message_size
- - static_cast<std::uint32_t>(_recv_buffer_size);
- } else if (VSOMEIP_SOMEIP_HEADER_SIZE > _recv_buffer_size) {
- its_missing_capacity = VSOMEIP_SOMEIP_HEADER_SIZE
- - static_cast<std::uint32_t>(_recv_buffer_size);
} else if (has_enabled_magic_cookies_ && _recv_buffer_size > 0) {
- uint32_t its_offset = find_magic_cookie(&(*_recv_buffer)[its_iteration_gap], _recv_buffer_size);
+ const uint32_t its_offset = find_magic_cookie(
+ &(*_recv_buffer)[its_iteration_gap], _recv_buffer_size);
if (its_offset < _recv_buffer_size) {
_recv_buffer_size -= its_offset;
its_iteration_gap += its_offset;
has_full_message = true; // trigger next loop
- } else {
+ VSOMEIP_ERROR << "Detected Magic Cookie within message data."
+ << " Resyncing. local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ }
+ }
+
+ if (!has_full_message) {
+ if (_recv_buffer_size > VSOMEIP_RETURN_CODE_POS &&
+ ((*recv_buffer_)[its_iteration_gap + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION ||
+ !utility::is_valid_message_type(static_cast<message_type_e>((*recv_buffer_)[its_iteration_gap + VSOMEIP_MESSAGE_TYPE_POS])) ||
+ !utility::is_valid_return_code(static_cast<return_code_e>((*recv_buffer_)[its_iteration_gap + VSOMEIP_RETURN_CODE_POS]))
+ )) {
+ if ((*recv_buffer_)[its_iteration_gap + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION) {
+ VSOMEIP_ERROR << "tce: Wrong protocol version: 0x"
+ << std::hex << std::setw(2) << std::setfill('0')
+ << std::uint32_t((*recv_buffer_)[its_iteration_gap + VSOMEIP_PROTOCOL_VERSION_POS])
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ // ensure to send back a message w/ wrong protocol version
+ its_lock.unlock();
+ its_host->on_message(&(*_recv_buffer)[its_iteration_gap],
+ VSOMEIP_SOMEIP_HEADER_SIZE + 8, this,
+ boost::asio::ip::address(),
+ VSOMEIP_ROUTING_CLIENT,
+ remote_address_,
+ remote_port_);
+ its_lock.lock();
+ } else if (!utility::is_valid_message_type(static_cast<message_type_e>(
+ (*recv_buffer_)[its_iteration_gap + VSOMEIP_MESSAGE_TYPE_POS]))) {
+ VSOMEIP_ERROR << "tce: Invalid message type: 0x"
+ << std::hex << std::setw(2) << std::setfill('0')
+ << std::uint32_t((*recv_buffer_)[its_iteration_gap + VSOMEIP_MESSAGE_TYPE_POS])
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ } else if (!utility::is_valid_return_code(static_cast<return_code_e>(
+ (*recv_buffer_)[its_iteration_gap + VSOMEIP_RETURN_CODE_POS]))) {
+ VSOMEIP_ERROR << "tce: Invalid return code: 0x"
+ << std::hex << std::setw(2) << std::setfill('0')
+ << std::uint32_t((*recv_buffer_)[its_iteration_gap + VSOMEIP_RETURN_CODE_POS])
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ }
+ state_ = cei_state_e::CONNECTING;
+ shutdown_and_close_socket_unlocked(false);
+ its_lock.unlock();
+ its_host->on_disconnect(shared_from_this());
+ restart(true);
+ return;
+ } else if (max_message_size_ != MESSAGE_SIZE_UNLIMITED &&
+ current_message_size > max_message_size_) {
+ _recv_buffer_size = 0;
+ _recv_buffer->resize(recv_buffer_size_initial_, 0x0);
+ _recv_buffer->shrink_to_fit();
+ if (has_enabled_magic_cookies_) {
+ VSOMEIP_ERROR << "Received a TCP message which exceeds "
+ << "maximum message size ("
+ << std::dec << current_message_size
+ << "). Magic Cookies are enabled: "
+ << "Resetting receiver. local: "
+ << get_address_port_local() << " remote: "
+ << get_address_port_remote();
+ } else {
+ VSOMEIP_ERROR << "Received a TCP message which exceeds "
+ << "maximum message size ("
+ << std::dec << current_message_size
+ << ") Magic cookies are disabled, "
+ << "Restarting connection. "
+ << "local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ state_ = cei_state_e::CONNECTING;
+ shutdown_and_close_socket_unlocked(false);
+ its_lock.unlock();
+ its_host->on_disconnect(shared_from_this());
+ restart(true);
+ return;
+ }
+ } else if (current_message_size > _recv_buffer_size) {
+ its_missing_capacity = current_message_size
+ - static_cast<std::uint32_t>(_recv_buffer_size);
+ } else if (VSOMEIP_SOMEIP_HEADER_SIZE > _recv_buffer_size) {
+ its_missing_capacity = VSOMEIP_SOMEIP_HEADER_SIZE
+ - static_cast<std::uint32_t>(_recv_buffer_size);
+ } else if (has_enabled_magic_cookies_ && _recv_buffer_size > 0) {
+ // no need to check for magic cookie here again: has_full_message
+ // would have been set to true if there was one present in the data
_recv_buffer_size = 0;
+ _recv_buffer->resize(recv_buffer_size_initial_, 0x0);
+ _recv_buffer->shrink_to_fit();
its_missing_capacity = 0;
+ VSOMEIP_ERROR << "tce::c<" << this
+ << ">rcb: recv_buffer_capacity: "
+ << _recv_buffer->capacity()
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote()
+ << ". Didn't find magic cookie in broken data, trying to resync.";
+ } else {
+ VSOMEIP_ERROR << "tce::c<" << this
+ << ">rcb: recv_buffer_size is: " << std::dec
+ << _recv_buffer_size << " but couldn't read "
+ "out message_size. recv_buffer_capacity: "
+ << _recv_buffer->capacity()
+ << " its_iteration_gap: " << its_iteration_gap
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote()
+ << ". Restarting connection due to missing/broken data TCP stream.";
+ state_ = cei_state_e::CONNECTING;
+ shutdown_and_close_socket_unlocked(false);
+ its_lock.unlock();
+ its_host->on_disconnect(shared_from_this());
+ restart(true);
+ return;
}
- } else {
- VSOMEIP_ERROR << "tce::c<" << this
- << ">rcb: recv_buffer_size is: " << std::dec
- << _recv_buffer_size << " but couldn't read "
- "out message_size. recv_buffer_capacity: "
- << _recv_buffer->capacity()
- << " its_iteration_gap: " << its_iteration_gap
- << " local: " << get_address_port_local()
- << " remote: " << get_address_port_remote()
- << ". Restarting connection due to missing/broken data TCP stream.";
- its_lock.unlock();
- restart(true);
- return;
}
} while (has_full_message && _recv_buffer_size);
if (its_iteration_gap) {
@@ -555,7 +655,6 @@ void tcp_client_endpoint_impl::receive_cbk( VSOMEIP_WARNING << "tcp_client_endpoint receive_cbk restarting."; state_ = cei_state_e::CONNECTING;
shutdown_and_close_socket_unlocked(false);
- was_not_connected_ = true;
its_lock.unlock();
its_host->on_disconnect(shared_from_this());
restart(true);
@@ -705,7 +804,6 @@ void tcp_client_endpoint_impl::send_cbk(boost::system::error_code const &_error, } else {
state_ = cei_state_e::CONNECTING;
shutdown_and_close_socket(false);
- was_not_connected_ = true;
std::shared_ptr<endpoint_host> its_host = host_.lock();
if (its_host) {
its_host->on_disconnect(shared_from_this());
@@ -742,4 +840,12 @@ void tcp_client_endpoint_impl::send_cbk(boost::system::error_code const &_error, }
}
+std::uint32_t tcp_client_endpoint_impl::get_max_allowed_reconnects() const {
+ return MAX_RECONNECTS_UNLIMITED;
+}
+
+void tcp_client_endpoint_impl::max_allowed_reconnects_reached() {
+ return;
+}
+
} // namespace vsomeip
diff --git a/implementation/endpoints/src/tcp_server_endpoint_impl.cpp b/implementation/endpoints/src/tcp_server_endpoint_impl.cpp index 9c2998f..30402c5 100644 --- a/implementation/endpoints/src/tcp_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/tcp_server_endpoint_impl.cpp @@ -188,6 +188,21 @@ void tcp_server_endpoint_impl::accept_cbk(connection::ptr _connection, && _error != boost::asio::error::operation_aborted && _error != boost::asio::error::no_descriptors) { start(); + } else if (_error == boost::asio::error::no_descriptors) { + VSOMEIP_ERROR<< "tcp_server_endpoint_impl::accept_cbk: " + << _error.message() << " (" << std::dec << _error.value() + << ") Will try to accept again in 1000ms"; + std::shared_ptr<boost::asio::steady_timer> its_timer = + std::make_shared<boost::asio::steady_timer>(service_, + std::chrono::milliseconds(1000)); + auto its_ep = std::dynamic_pointer_cast<tcp_server_endpoint_impl>( + shared_from_this()); + its_timer->async_wait([its_timer, its_ep] + (const boost::system::error_code& _error) { + if (!_error) { + its_ep->start(); + } + }); } } @@ -205,7 +220,7 @@ bool tcp_server_endpoint_impl::is_reliable() const { tcp_server_endpoint_impl::connection::connection( std::weak_ptr<tcp_server_endpoint_impl> _server, std::uint32_t _max_message_size, - std::uint32_t _initial_recv_buffer_size, + std::uint32_t _recv_buffer_size_initial, std::uint32_t _buffer_shrink_threshold, bool _magic_cookies_enabled, boost::asio::io_service &_io_service, @@ -213,8 +228,8 @@ tcp_server_endpoint_impl::connection::connection( socket_(_io_service), server_(_server), max_message_size_(_max_message_size), - recv_buffer_size_initial_(_initial_recv_buffer_size), - recv_buffer_(_initial_recv_buffer_size, 0), + recv_buffer_size_initial_(_recv_buffer_size_initial), + recv_buffer_(_recv_buffer_size_initial, 0), recv_buffer_size_(0), missing_capacity_(0), shrink_count_(0), @@ -235,8 +250,7 @@ tcp_server_endpoint_impl::connection::create( boost::asio::io_service & _io_service, std::chrono::milliseconds _send_timeout) { const std::uint32_t its_initial_receveive_buffer_size = - VSOMEIP_SOMEIP_HEADER_SIZE + 8 + MAGIC_COOKIE_SIZE + 8 - + VSOMEIP_MAX_TCP_MESSAGE_SIZE; + VSOMEIP_SOMEIP_HEADER_SIZE + 8 + MAGIC_COOKIE_SIZE + 8; return ptr(new connection(_server, _max_message_size, its_initial_receveive_buffer_size, _buffer_shrink_threshold, _magic_cookies_enabled, @@ -272,6 +286,12 @@ void tcp_server_endpoint_impl::connection::receive() { if (its_capacity < its_required_capacity) { recv_buffer_.reserve(its_required_capacity); recv_buffer_.resize(its_required_capacity, 0x0); + if (recv_buffer_.size() > 1048576) { + VSOMEIP_INFO << "tse: recv_buffer size is: " << + recv_buffer_.size() + << " local: " << get_address_port_local() + << " remote: " << get_address_port_remote(); + } } buffer_size = missing_capacity_; missing_capacity_ = 0; @@ -412,7 +432,7 @@ void tcp_server_endpoint_impl::connection::receive_cbk( return; } uint32_t current_message_size = static_cast<uint32_t>(read_message_size); - has_full_message = (current_message_size > VSOMEIP_SOMEIP_HEADER_SIZE + has_full_message = (current_message_size > VSOMEIP_RETURN_CODE_POS && current_message_size <= recv_buffer_size_); if (has_full_message) { bool needs_forwarding(true); @@ -424,7 +444,12 @@ void tcp_server_endpoint_impl::connection::receive_cbk( = its_server->find_magic_cookie(&recv_buffer_[its_iteration_gap], recv_buffer_size_); if (its_offset < current_message_size) { - VSOMEIP_ERROR << "Detected Magic Cookie within message data. Resyncing."; + { + std::lock_guard<std::mutex> its_lock(socket_mutex_); + VSOMEIP_ERROR << "Detected Magic Cookie within message data. Resyncing." + << " local: " << get_address_port_local() + << " remote: " << get_address_port_remote(); + } if (!is_magic_cookie(its_iteration_gap)) { its_host->on_error(&recv_buffer_[its_iteration_gap], static_cast<length_t>(recv_buffer_size_),its_server.get(), @@ -443,14 +468,16 @@ void tcp_server_endpoint_impl::connection::receive_cbk( std::memcpy(&its_client, &recv_buffer_[its_iteration_gap + VSOMEIP_CLIENT_POS_MIN], sizeof(client_t)); - session_t its_session; - std::memcpy(&its_session, - &recv_buffer_[its_iteration_gap + VSOMEIP_SESSION_POS_MIN], - sizeof(session_t)); - its_server->clients_mutex_.lock(); - its_server->clients_[its_client][its_session] = remote_; - its_server->endpoint_to_client_[remote_] = its_client; - its_server->clients_mutex_.unlock(); + if (its_client != MAGIC_COOKIE_NETWORK_BYTE_ORDER) { + session_t its_session; + std::memcpy(&its_session, + &recv_buffer_[its_iteration_gap + VSOMEIP_SESSION_POS_MIN], + sizeof(session_t)); + its_server->clients_mutex_.lock(); + its_server->clients_[its_client][its_session] = remote_; + its_server->endpoint_to_client_[remote_] = its_client; + its_server->clients_mutex_.unlock(); + } } if (!magic_cookies_enabled_) { its_host->on_message(&recv_buffer_[its_iteration_gap], @@ -478,7 +505,12 @@ void tcp_server_endpoint_impl::connection::receive_cbk( its_server->find_magic_cookie(&recv_buffer_[its_iteration_gap], recv_buffer_size_); if (its_offset < recv_buffer_size_) { - VSOMEIP_ERROR << "Detected Magic Cookie within message data. Resyncing."; + { + std::lock_guard<std::mutex> its_lock(socket_mutex_); + VSOMEIP_ERROR << "Detected Magic Cookie within message data. Resyncing." + << " local: " << get_address_port_local() + << " remote: " << get_address_port_remote(); + } if (!is_magic_cookie(its_iteration_gap)) { its_host->on_error(&recv_buffer_[its_iteration_gap], static_cast<length_t>(recv_buffer_size_), its_server.get(), @@ -496,7 +528,53 @@ void tcp_server_endpoint_impl::connection::receive_cbk( } if (!has_full_message) { - if (max_message_size_ != MESSAGE_SIZE_UNLIMITED + if (recv_buffer_size_ > VSOMEIP_RETURN_CODE_POS && + (recv_buffer_[its_iteration_gap + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION || + !utility::is_valid_message_type(static_cast<message_type_e>(recv_buffer_[its_iteration_gap + VSOMEIP_MESSAGE_TYPE_POS])) || + !utility::is_valid_return_code(static_cast<return_code_e>(recv_buffer_[its_iteration_gap + VSOMEIP_RETURN_CODE_POS])) + )) { + if (recv_buffer_[its_iteration_gap + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION) { + { + std::lock_guard<std::mutex> its_lock(socket_mutex_); + VSOMEIP_ERROR << "tse: Wrong protocol version: 0x" + << std::hex << std::setw(2) << std::setfill('0') + << std::uint32_t(recv_buffer_[its_iteration_gap + VSOMEIP_PROTOCOL_VERSION_POS]) + << " local: " << get_address_port_local() + << " remote: " << get_address_port_remote() + << ". Closing connection due to missing/broken data TCP stream."; + } + // ensure to send back a error message w/ wrong protocol version + its_host->on_message(&recv_buffer_[its_iteration_gap], + VSOMEIP_SOMEIP_HEADER_SIZE + 8, its_server.get(), + boost::asio::ip::address(), + VSOMEIP_ROUTING_CLIENT, + remote_address_, remote_port_); + } else if (!utility::is_valid_message_type(static_cast<message_type_e>( + recv_buffer_[its_iteration_gap + VSOMEIP_MESSAGE_TYPE_POS]))) { + std::lock_guard<std::mutex> its_lock(socket_mutex_); + VSOMEIP_ERROR << "tse: Invalid message type: 0x" + << std::hex << std::setw(2) << std::setfill('0') + << std::uint32_t(recv_buffer_[its_iteration_gap + VSOMEIP_MESSAGE_TYPE_POS]) + << " local: " << get_address_port_local() + << " remote: " << get_address_port_remote() + << ". Closing connection due to missing/broken data TCP stream."; + } else if (!utility::is_valid_return_code(static_cast<return_code_e>( + recv_buffer_[its_iteration_gap + VSOMEIP_RETURN_CODE_POS]))) { + std::lock_guard<std::mutex> its_lock(socket_mutex_); + VSOMEIP_ERROR << "tse: Invalid return code: 0x" + << std::hex << std::setw(2) << std::setfill('0') + << std::uint32_t(recv_buffer_[its_iteration_gap + VSOMEIP_RETURN_CODE_POS]) + << " local: " << get_address_port_local() + << " remote: " << get_address_port_remote() + << ". Closing connection due to missing/broken data TCP stream."; + } + { + std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); + stop(); + } + its_server->remove_connection(this); + return; + } else if (max_message_size_ != MESSAGE_SIZE_UNLIMITED && current_message_size > max_message_size_) { std::lock_guard<std::mutex> its_lock(socket_mutex_); recv_buffer_size_ = 0; @@ -517,9 +595,14 @@ void tcp_server_endpoint_impl::connection::receive_cbk( << std::dec << current_message_size << " > " << std::dec << max_message_size_ << ") Magic cookies are disabled: " - << "Connection will be disabled! local: " + << "Connection will be closed! local: " << get_address_port_local() << " remote: " << get_address_port_remote(); + { + std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); + stop(); + } + its_server->remove_connection(this); return; } } else if (current_message_size > recv_buffer_size_) { @@ -528,6 +611,18 @@ void tcp_server_endpoint_impl::connection::receive_cbk( } else if (VSOMEIP_SOMEIP_HEADER_SIZE > recv_buffer_size_) { missing_capacity_ = VSOMEIP_SOMEIP_HEADER_SIZE - static_cast<std::uint32_t>(recv_buffer_size_); + } else if (magic_cookies_enabled_ && recv_buffer_size_ > 0) { + // no need to check for magic cookie here again: has_full_message + // would have been set to true if there was one present in the data + recv_buffer_size_ = 0; + recv_buffer_.resize(recv_buffer_size_initial_, 0x0); + recv_buffer_.shrink_to_fit(); + missing_capacity_ = 0; + std::lock_guard<std::mutex> its_lock(socket_mutex_); + VSOMEIP_ERROR << "Didn't find magic cookie in broken" + << " data, trying to resync." + << " local: " << get_address_port_local() + << " remote: " << get_address_port_remote(); } else { { std::lock_guard<std::mutex> its_lock(socket_mutex_); diff --git a/implementation/endpoints/src/udp_client_endpoint_impl.cpp b/implementation/endpoints/src/udp_client_endpoint_impl.cpp index 7be55f4..67c9703 100644 --- a/implementation/endpoints/src/udp_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/udp_client_endpoint_impl.cpp @@ -20,12 +20,13 @@ udp_client_endpoint_impl::udp_client_endpoint_impl( endpoint_type _local,
endpoint_type _remote,
boost::asio::io_service &_io,
- configuration::endpoint_queue_limit_t _queue_limit)
+ configuration::endpoint_queue_limit_t _queue_limit,
+ std::uint32_t _udp_receive_buffer_size)
: udp_client_endpoint_base_impl(_host, _local, _remote, _io,
VSOMEIP_MAX_UDP_MESSAGE_SIZE, _queue_limit),
- recv_buffer_(VSOMEIP_MAX_UDP_MESSAGE_SIZE, 0),
remote_address_(_remote.address()),
- remote_port_(_remote.port()) {
+ remote_port_(_remote.port()),
+ udp_receive_buffer_size_(_udp_receive_buffer_size) {
}
udp_client_endpoint_impl::~udp_client_endpoint_impl() {
@@ -53,6 +54,26 @@ void udp_client_endpoint_impl::connect() { << "SO_REUSEADDR: " << its_error.message() << " remote:"
<< get_address_port_remote();
}
+ socket_->set_option(boost::asio::socket_base::receive_buffer_size(
+ udp_receive_buffer_size_), its_error);
+ if (its_error) {
+ VSOMEIP_WARNING << "udp_client_endpoint_impl::connect: couldn't set "
+ << "SO_RCVBUF: " << its_error.message() << " to: "
+ << std::dec << udp_receive_buffer_size_ << " remote:"
+ << get_address_port_remote();
+ } else {
+ boost::asio::socket_base::receive_buffer_size its_option;
+ socket_->get_option(its_option, its_error);
+ if (its_error) {
+ VSOMEIP_WARNING << "udp_client_endpoint_impl::connect: couldn't get "
+ << "SO_RCVBUF: " << its_error.message() << " remote:"
+ << get_address_port_remote();
+ } else {
+ VSOMEIP_INFO << "udp_client_endpoint_impl::connect: SO_RCVBUF is: "
+ << std::dec << its_option.value();
+ }
+ }
+
// In case a client endpoint port was configured,
// bind to it before connecting
if (local_.port() != ILLEGAL_PORT) {
@@ -64,7 +85,7 @@ void udp_client_endpoint_impl::connect() { << " remote:" << get_address_port_remote();
try {
// don't connect on bind error to avoid using a random port
- service_.post(std::bind(&client_endpoint_impl::connect_cbk,
+ strand_.post(std::bind(&client_endpoint_impl::connect_cbk, shared_from_this(), its_bind_error));
} catch (const std::exception &e) {
VSOMEIP_ERROR << "udp_client_endpoint_impl::connect: "
@@ -76,15 +97,19 @@ void udp_client_endpoint_impl::connect() { state_ = cei_state_e::CONNECTING;
socket_->async_connect(
remote_,
- std::bind(
- &udp_client_endpoint_base_impl::connect_cbk,
- shared_from_this(),
- std::placeholders::_1
+ strand_.wrap( + std::bind( + &udp_client_endpoint_base_impl::connect_cbk, + shared_from_this(), + std::placeholders::_1 + ) )
);
} else {
VSOMEIP_WARNING << "udp_client_endpoint::connect: Error opening socket: "
<< its_error.message() << " remote:" << get_address_port_remote();
+ strand_.post(std::bind(&udp_client_endpoint_base_impl::connect_cbk,
+ shared_from_this(), its_error));
}
}
@@ -107,6 +132,8 @@ void udp_client_endpoint_impl::restart(bool _force) { local = get_address_port_local();
}
shutdown_and_close_socket(false);
+ was_not_connected_ = true;
+ reconnect_counter_ = 0;
VSOMEIP_WARNING << "uce::restart: local: " << local
<< " remote: " << get_address_port_remote();
start_connect_timer();
@@ -148,16 +175,20 @@ void udp_client_endpoint_impl::receive() { if (!socket_->is_open()) {
return;
}
+ message_buffer_ptr_t its_buffer = std::make_shared<message_buffer_t>(VSOMEIP_MAX_UDP_MESSAGE_SIZE);
socket_->async_receive_from(
- boost::asio::buffer(&recv_buffer_[0], max_message_size_),
+ boost::asio::buffer(*its_buffer),
remote_,
- std::bind(
- &udp_client_endpoint_impl::receive_cbk,
- std::dynamic_pointer_cast<
- udp_client_endpoint_impl
- >(shared_from_this()),
- std::placeholders::_1,
- std::placeholders::_2
+ strand_.wrap(
+ std::bind(
+ &udp_client_endpoint_impl::receive_cbk,
+ std::dynamic_pointer_cast<
+ udp_client_endpoint_impl
+ >(shared_from_this()),
+ std::placeholders::_1,
+ std::placeholders::_2,
+ its_buffer
+ )
)
);
}
@@ -190,7 +221,8 @@ std::uint16_t udp_client_endpoint_impl::get_remote_port() const { }
void udp_client_endpoint_impl::receive_cbk(
- boost::system::error_code const &_error, std::size_t _bytes) {
+ boost::system::error_code const &_error, std::size_t _bytes,
+ message_buffer_ptr_t _recv_buffer) {
if (_error == boost::asio::error::operation_aborted) {
// endpoint was stopped
return;
@@ -202,7 +234,7 @@ void udp_client_endpoint_impl::receive_cbk( msg << "ucei::rcb(" << _error.message() << "): ";
for (std::size_t i = 0; i < _bytes + recv_buffer_size_; ++i)
msg << std::hex << std::setw(2) << std::setfill('0')
- << (int) recv_buffer_[i] << " ";
+ << (int) (*_recv_buffer)[i] << " ";
VSOMEIP_INFO << msg.str();
#endif
std::size_t remaining_bytes = _bytes;
@@ -210,7 +242,7 @@ void udp_client_endpoint_impl::receive_cbk( do {
uint64_t read_message_size
- = utility::get_message_size(&this->recv_buffer_[i],
+ = utility::get_message_size(&(*_recv_buffer)[i],
remaining_bytes);
if (read_message_size > MESSAGE_SIZE_UNLIMITED) {
VSOMEIP_ERROR << "Message size exceeds allowed maximum!";
@@ -222,10 +254,45 @@ void udp_client_endpoint_impl::receive_cbk( if (remaining_bytes - current_message_size > remaining_bytes) {
VSOMEIP_ERROR << "buffer underflow in udp client endpoint ~> abort!";
return;
+ } else if (current_message_size > VSOMEIP_RETURN_CODE_POS &&
+ ((*_recv_buffer)[i + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION ||
+ !utility::is_valid_message_type(static_cast<message_type_e>((*_recv_buffer)[i + VSOMEIP_MESSAGE_TYPE_POS])) ||
+ !utility::is_valid_return_code(static_cast<return_code_e>((*_recv_buffer)[i + VSOMEIP_RETURN_CODE_POS]))
+ )) {
+ if ((*_recv_buffer)[i + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION) {
+ VSOMEIP_ERROR << "uce: Wrong protocol version: 0x"
+ << std::hex << std::setw(2) << std::setfill('0')
+ << std::uint32_t((*_recv_buffer)[i + VSOMEIP_PROTOCOL_VERSION_POS])
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ // ensure to send back a message w/ wrong protocol version
+ its_host->on_message(&(*_recv_buffer)[i],
+ VSOMEIP_SOMEIP_HEADER_SIZE + 8, this,
+ boost::asio::ip::address(),
+ VSOMEIP_ROUTING_CLIENT,
+ remote_address_,
+ remote_port_);
+ } else if (!utility::is_valid_message_type(static_cast<message_type_e>(
+ (*_recv_buffer)[i + VSOMEIP_MESSAGE_TYPE_POS]))) {
+ VSOMEIP_ERROR << "uce: Invalid message type: 0x"
+ << std::hex << std::setw(2) << std::setfill('0')
+ << std::uint32_t((*_recv_buffer)[i + VSOMEIP_MESSAGE_TYPE_POS])
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ } else if (!utility::is_valid_return_code(static_cast<return_code_e>(
+ (*_recv_buffer)[i + VSOMEIP_RETURN_CODE_POS]))) {
+ VSOMEIP_ERROR << "uce: Invalid return code: 0x"
+ << std::hex << std::setw(2) << std::setfill('0')
+ << std::uint32_t((*_recv_buffer)[i + VSOMEIP_RETURN_CODE_POS])
+ << " local: " << get_address_port_local()
+ << " remote: " << get_address_port_remote();
+ }
+ receive();
+ return;
}
remaining_bytes -= current_message_size;
- its_host->on_message(&recv_buffer_[i], current_message_size,
+ its_host->on_message(&(*_recv_buffer)[i], current_message_size,
this, boost::asio::ip::address(),
VSOMEIP_ROUTING_CLIENT, remote_address_,
remote_port_);
@@ -304,4 +371,12 @@ std::string udp_client_endpoint_impl::get_remote_information() const { + std::to_string(remote_.port());
}
+std::uint32_t udp_client_endpoint_impl::get_max_allowed_reconnects() const {
+ return MAX_RECONNECTS_UNLIMITED;
+}
+
+void udp_client_endpoint_impl::max_allowed_reconnects_reached() {
+ return;
+}
+
} // namespace vsomeip
diff --git a/implementation/endpoints/src/udp_server_endpoint_impl.cpp b/implementation/endpoints/src/udp_server_endpoint_impl.cpp index 6da5b32..6ad7ce8 100644 --- a/implementation/endpoints/src/udp_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/udp_server_endpoint_impl.cpp @@ -8,6 +8,8 @@ #include <boost/asio/ip/multicast.hpp> +#include <vsomeip/constants.hpp> + #include "../include/endpoint_definition.hpp" #include "../include/endpoint_host.hpp" #include "../include/udp_server_endpoint_impl.hpp" @@ -25,7 +27,8 @@ udp_server_endpoint_impl::udp_server_endpoint_impl( std::shared_ptr< endpoint_host > _host, endpoint_type _local, boost::asio::io_service &_io, - configuration::endpoint_queue_limit_t _queue_limit) + configuration::endpoint_queue_limit_t _queue_limit, + std::uint32_t _udp_receive_buffer_size) : server_endpoint_impl<ip::udp_ext>( _host, _local, _io, VSOMEIP_MAX_UDP_MESSAGE_SIZE, _queue_limit), socket_(_io, _local.protocol()), @@ -60,6 +63,27 @@ udp_server_endpoint_impl::udp_server_endpoint_impl( socket_.set_option(option, ec); boost::asio::detail::throw_error(ec, "broadcast option"); + socket_.set_option(boost::asio::socket_base::receive_buffer_size( + _udp_receive_buffer_size), ec); + if (ec) { + VSOMEIP_WARNING << "udp_server_endpoint_impl:: couldn't set " + << "SO_RCVBUF: " << ec.message() << " to: " << std::dec + << _udp_receive_buffer_size << " local port: " << std::dec + << local_port_; + } else { + boost::asio::socket_base::receive_buffer_size its_option; + socket_.get_option(its_option, ec); + if (ec) { + VSOMEIP_WARNING << "udp_server_endpoint_impl: couldn't get " + << "SO_RCVBUF: " << ec.message() << " local port:" + << std::dec << local_port_; + } else { + VSOMEIP_INFO << "udp_server_endpoint_impl: SO_RCVBUF is: " + << std::dec << its_option.value(); + } + } + + #ifdef _WIN32 const char* optval("0001"); ::setsockopt(socket_.native(), IPPROTO_IP, IP_PKTINFO, @@ -167,8 +191,7 @@ bool udp_server_endpoint_impl::is_joined( void udp_server_endpoint_impl::join(const std::string &_address) { bool has_received(false); - std::function<void(const std::string &)> join_func = - [this](const std::string &_address) { + auto join_func = [this](const std::string &_address) { try { bool is_v4(false); bool is_v6(false); @@ -318,6 +341,41 @@ void udp_server_endpoint_impl::receive_cbk( if (remaining_bytes - current_message_size > remaining_bytes) { VSOMEIP_ERROR << "buffer underflow in udp client endpoint ~> abort!"; return; + } else if (current_message_size > VSOMEIP_RETURN_CODE_POS && + (recv_buffer_[i + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION || + !utility::is_valid_message_type(static_cast<message_type_e>(recv_buffer_[i + VSOMEIP_MESSAGE_TYPE_POS])) || + !utility::is_valid_return_code(static_cast<return_code_e>(recv_buffer_[i + VSOMEIP_RETURN_CODE_POS])) + )) { + if (recv_buffer_[i + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION) { + VSOMEIP_ERROR << "use: Wrong protocol version: 0x" + << std::hex << std::setw(2) << std::setfill('0') + << std::uint32_t(recv_buffer_[i + VSOMEIP_PROTOCOL_VERSION_POS]) + << " local: " << get_address_port_local() + << " remote: " << its_remote_address << ":" << std::dec << its_remote_port; + // ensure to send back a message w/ wrong protocol version + its_host->on_message(&recv_buffer_[i], + VSOMEIP_SOMEIP_HEADER_SIZE + 8, this, + _destination, + VSOMEIP_ROUTING_CLIENT, + its_remote_address, + its_remote_port); + } else if (!utility::is_valid_message_type(static_cast<message_type_e>( + recv_buffer_[i + VSOMEIP_MESSAGE_TYPE_POS]))) { + VSOMEIP_ERROR << "use: Invalid message type: 0x" + << std::hex << std::setw(2) << std::setfill('0') + << std::uint32_t(recv_buffer_[i + VSOMEIP_MESSAGE_TYPE_POS]) + << " local: " << get_address_port_local() + << " remote: " << its_remote_address << ":" << std::dec << its_remote_port; + } else if (!utility::is_valid_return_code(static_cast<return_code_e>( + recv_buffer_[i + VSOMEIP_RETURN_CODE_POS]))) { + VSOMEIP_ERROR << "use: Invalid return code: 0x" + << std::hex << std::setw(2) << std::setfill('0') + << std::uint32_t(recv_buffer_[i + VSOMEIP_RETURN_CODE_POS]) + << " local: " << get_address_port_local() + << " remote: " << its_remote_address << ":" << std::dec << its_remote_port; + } + receive(); + return; } remaining_bytes -= current_message_size; service_t its_service = VSOMEIP_BYTES_TO_WORD(recv_buffer_[i + VSOMEIP_SERVICE_POS_MIN], @@ -328,14 +386,16 @@ void udp_server_endpoint_impl::receive_cbk( std::memcpy(&its_client, &recv_buffer_[i + VSOMEIP_CLIENT_POS_MIN], sizeof(client_t)); - session_t its_session; - std::memcpy(&its_session, - &recv_buffer_[i + VSOMEIP_SESSION_POS_MIN], - sizeof(session_t)); - clients_mutex_.lock(); - clients_[its_client][its_session] = remote_; - endpoint_to_client_[remote_] = its_client; - clients_mutex_.unlock(); + if (its_client != MAGIC_COOKIE_NETWORK_BYTE_ORDER) { + session_t its_session; + std::memcpy(&its_session, + &recv_buffer_[i + VSOMEIP_SESSION_POS_MIN], + sizeof(session_t)); + clients_mutex_.lock(); + clients_[its_client][its_session] = remote_; + endpoint_to_client_[remote_] = its_client; + clients_mutex_.unlock(); + } } else if (its_service != VSOMEIP_SD_SERVICE && utility::is_notification(recv_buffer_[i + VSOMEIP_MESSAGE_TYPE_POS]) && joined_group_) { @@ -424,4 +484,20 @@ std::string udp_server_endpoint_impl::get_remote_information( + std::to_string(_queue_iterator->first.port()); } +const std::string udp_server_endpoint_impl::get_address_port_local() const { + std::lock_guard<std::mutex> its_lock(socket_mutex_); + std::string its_address_port; + its_address_port.reserve(21); + boost::system::error_code ec; + if (socket_.is_open()) { + endpoint_type its_local_endpoint = socket_.local_endpoint(ec); + if (!ec) { + its_address_port += its_local_endpoint.address().to_string(ec); + its_address_port += ":"; + its_address_port += std::to_string(its_local_endpoint.port()); + } + } + return its_address_port; +} + } // namespace vsomeip diff --git a/implementation/endpoints/src/virtual_server_endpoint_impl.cpp b/implementation/endpoints/src/virtual_server_endpoint_impl.cpp index 30862df..20d40d7 100644 --- a/implementation/endpoints/src/virtual_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/virtual_server_endpoint_impl.cpp @@ -24,10 +24,14 @@ void virtual_server_endpoint_impl::start() { void virtual_server_endpoint_impl::stop() { } -bool virtual_server_endpoint_impl::is_connected() const { +bool virtual_server_endpoint_impl::is_established() const { return false; } +void virtual_server_endpoint_impl::set_established(bool _established) { + (void) _established; +} + void virtual_server_endpoint_impl::set_connected(bool _connected) { (void) _connected; } diff --git a/implementation/message/include/deserializer.hpp b/implementation/message/include/deserializer.hpp index 7449f9b..4c01c79 100644 --- a/implementation/message/include/deserializer.hpp +++ b/implementation/message/include/deserializer.hpp @@ -29,7 +29,7 @@ public: VSOMEIP_EXPORT std::size_t get_available() const;
VSOMEIP_EXPORT std::size_t get_remaining() const;
- VSOMEIP_EXPORT void set_remaining(std::size_t _length);
+ VSOMEIP_EXPORT void set_remaining(std::size_t _remaining);
// to be used by applications to deserialize a message
VSOMEIP_EXPORT message * deserialize_message();
@@ -41,6 +41,7 @@ public: VSOMEIP_EXPORT bool deserialize(uint32_t& _value,
bool _omit_last_byte = false);
VSOMEIP_EXPORT bool deserialize(uint8_t *_data, std::size_t _length);
+ VSOMEIP_EXPORT bool deserialize(std::string& _target, std::size_t _length);
VSOMEIP_EXPORT bool deserialize(std::vector<uint8_t>& _value);
VSOMEIP_EXPORT bool look_ahead(std::size_t _index, uint8_t &_value) const;
diff --git a/implementation/message/include/message_base_impl.hpp b/implementation/message/include/message_base_impl.hpp index ad42261..efd5ffe 100644 --- a/implementation/message/include/message_base_impl.hpp +++ b/implementation/message/include/message_base_impl.hpp @@ -42,10 +42,10 @@ public: VSOMEIP_EXPORT void set_session(session_t _session);
VSOMEIP_EXPORT protocol_version_t get_protocol_version() const;
- VSOMEIP_EXPORT void set_protocol_version(protocol_version_t _version);
+ VSOMEIP_EXPORT void set_protocol_version(protocol_version_t _protocol_version);
VSOMEIP_EXPORT interface_version_t get_interface_version() const;
- VSOMEIP_EXPORT void set_interface_version(interface_version_t _version);
+ VSOMEIP_EXPORT void set_interface_version(interface_version_t _interface_version);
VSOMEIP_EXPORT message_type_e get_message_type() const;
VSOMEIP_EXPORT void set_message_type(message_type_e _type);
diff --git a/implementation/message/include/message_header_impl.hpp b/implementation/message/include/message_header_impl.hpp index 5768bbe..8cd4b00 100644 --- a/implementation/message/include/message_header_impl.hpp +++ b/implementation/message/include/message_header_impl.hpp @@ -18,7 +18,7 @@ class message_base; class message_header_impl: virtual public serializable {
public:
VSOMEIP_EXPORT message_header_impl();
- VSOMEIP_EXPORT message_header_impl(const message_header_impl& header);
+ VSOMEIP_EXPORT message_header_impl(const message_header_impl& _header);
VSOMEIP_EXPORT bool serialize(serializer *_to) const;
VSOMEIP_EXPORT bool deserialize(deserializer *_from);
diff --git a/implementation/message/include/payload_impl.hpp b/implementation/message/include/payload_impl.hpp index f9b05a2..2eb67e9 100644 --- a/implementation/message/include/payload_impl.hpp +++ b/implementation/message/include/payload_impl.hpp @@ -18,7 +18,7 @@ class payload_impl: public payload { public:
VSOMEIP_EXPORT payload_impl();
VSOMEIP_EXPORT payload_impl(const byte_t *_data, uint32_t _size);
- VSOMEIP_EXPORT payload_impl(const std::vector< byte_t > &_value);
+ VSOMEIP_EXPORT payload_impl(const std::vector< byte_t > &_data);
VSOMEIP_EXPORT payload_impl(const payload_impl& _payload);
VSOMEIP_EXPORT virtual ~payload_impl();
diff --git a/implementation/message/src/deserializer.cpp b/implementation/message/src/deserializer.cpp index 8a3c891..b416199 100644 --- a/implementation/message/src/deserializer.cpp +++ b/implementation/message/src/deserializer.cpp @@ -111,6 +111,17 @@ bool deserializer::deserialize(uint8_t *_data, std::size_t _length) { return true;
}
+bool deserializer::deserialize(std::string& _target, std::size_t _length) {
+ if (_length > remaining_ || _length > _target.capacity()) {
+ return false;
+ }
+ _target.assign(position_, position_ + _length);
+ position_ += _length;
+ remaining_ -= _length;
+
+ return true;
+}
+
bool deserializer::deserialize(std::vector< uint8_t >& _value) {
if (_value.capacity() > remaining_)
return false;
diff --git a/implementation/plugin/include/plugin_manager.hpp b/implementation/plugin/include/plugin_manager.hpp index 98afd72..7d2d7f0 100644 --- a/implementation/plugin/include/plugin_manager.hpp +++ b/implementation/plugin/include/plugin_manager.hpp @@ -46,7 +46,7 @@ private: std::map<plugin_type_e, std::map<std::string, std::shared_ptr<plugin> > > plugins_; std::map<plugin_type_e, std::map<std::string, void*> > handles_; - std::mutex plugins_mutex_; + std::recursive_mutex plugins_mutex_; static std::shared_ptr<plugin_manager> the_plugin_manager__; }; diff --git a/implementation/plugin/src/plugin_manager.cpp b/implementation/plugin/src/plugin_manager.cpp index e922fd6..3b9b3f7 100644 --- a/implementation/plugin/src/plugin_manager.cpp +++ b/implementation/plugin/src/plugin_manager.cpp @@ -62,7 +62,7 @@ void plugin_manager::load_plugins() { } } - std::lock_guard<std::mutex> its_lock_start_stop(plugins_mutex_); + std::lock_guard<std::recursive_mutex> its_lock_start_stop(plugins_mutex_); // Load plug-in info from libraries parsed before for (auto plugin_name : plugins) { void* handle = load_library(plugin_name); @@ -105,7 +105,7 @@ void plugin_manager::load_plugins() { } std::shared_ptr<plugin> plugin_manager::get_plugin(plugin_type_e _type, std::string _name) { - std::lock_guard<std::mutex> its_lock_start_stop(plugins_mutex_); + std::lock_guard<std::recursive_mutex> its_lock_start_stop(plugins_mutex_); auto its_type = plugins_.find(_type); if (its_type != plugins_.end()) { auto its_name = its_type->second.find(_name); @@ -142,7 +142,7 @@ std::shared_ptr<plugin> plugin_manager::load_plugin(const std::string _library, } bool plugin_manager::unload_plugin(plugin_type_e _type) { - std::lock_guard<std::mutex> its_lock_start_stop(plugins_mutex_); + std::lock_guard<std::recursive_mutex> its_lock_start_stop(plugins_mutex_); const auto found_handle = handles_.find(_type); if (found_handle != handles_.end()) { for (auto its_name : found_handle->second) { diff --git a/implementation/routing/include/event.hpp b/implementation/routing/include/event.hpp index 674f41b..668bbf4 100644 --- a/implementation/routing/include/event.hpp +++ b/implementation/routing/include/event.hpp @@ -72,7 +72,7 @@ public: void set_change_resets_cycle(bool _change_resets_cycle); // SIP_RPC_358 - void set_update_on_change(bool _is_on); + void set_update_on_change(bool _is_active); // SIP_RPC_359 (epsilon change) void set_epsilon_change_function(const epsilon_change_func_t &_epsilon_change_func); @@ -126,6 +126,9 @@ private: bool set_payload_helper(const std::shared_ptr<payload> &_payload, bool _force); void reset_payload(const std::shared_ptr<payload> &_payload); + void notify_one_unlocked(const std::shared_ptr<endpoint_definition> &_target, bool _flush); + void notify_one_unlocked(client_t _client, bool _flush); + private: routing_manager *routing_; mutable std::mutex mutex_; diff --git a/implementation/routing/include/routing_manager.hpp b/implementation/routing/include/routing_manager.hpp index f02c533..9ab09f4 100644 --- a/implementation/routing/include/routing_manager.hpp +++ b/implementation/routing/include/routing_manager.hpp @@ -15,6 +15,7 @@ #include <vsomeip/function_types.hpp> #include <vsomeip/message.hpp> #include <vsomeip/handler.hpp> +#include "types.hpp" namespace vsomeip { @@ -62,7 +63,9 @@ public: bool _flush) = 0; virtual bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush, bool _reliable, bool _is_valid_crc = true) = 0; + instance_t _instance, bool _flush, bool _reliable, + client_t _bound_client = VSOMEIP_ROUTING_CLIENT, + bool _is_valid_crc = true, bool _sent_from_remote = false) = 0; virtual bool send_to(const std::shared_ptr<endpoint_definition> &_target, std::shared_ptr<message>, bool _flush) = 0; @@ -94,7 +97,7 @@ public: virtual void notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, - client_t _client, bool _force, bool _flush) = 0; + client_t _client, bool _force, bool _flush, bool _remote_subscriber) = 0; virtual void on_identify_response(client_t _client, service_t _service, instance_t _instance, bool _reliable) = 0; diff --git a/implementation/routing/include/routing_manager_base.hpp b/implementation/routing/include/routing_manager_base.hpp index 7bf3a4d..6bee10c 100644 --- a/implementation/routing/include/routing_manager_base.hpp +++ b/implementation/routing/include/routing_manager_base.hpp @@ -23,18 +23,13 @@ #include "../../configuration/include/configuration.hpp" #include "../../endpoints/include/endpoint_host.hpp" +namespace vsomeip { #ifdef USE_DLT -#include "../../tracing/include/trace_connector.hpp" -#endif - -#ifdef USE_DLT -namespace tc { -class trace_connector; -} // namespace tc +namespace trace { +class connector_impl; +} // namespace trace #endif -namespace vsomeip { - class serializer; class routing_manager_base : public routing_manager, @@ -90,13 +85,15 @@ public: virtual void notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, - client_t _client, bool _force, bool _flush); + client_t _client, bool _force, bool _flush, bool _remote_subscriber); virtual bool send(client_t _client, std::shared_ptr<message> _message, bool _flush); virtual bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush, bool _reliable, bool _is_valid_crc = true) = 0; + instance_t _instance, bool _flush, bool _reliable, + client_t _bound_client = VSOMEIP_ROUTING_CLIENT, + bool _is_valid_crc = true, bool _sent_from_remote = false) = 0; // Endpoint host ~> will be implemented by routing_manager_impl/_proxy/ virtual void on_connect(std::shared_ptr<endpoint> _endpoint) = 0; @@ -125,6 +122,8 @@ public: virtual void send_get_offered_services_info(client_t _client, offer_type_e _offer_type) = 0; + std::set<client_t> find_local_clients(service_t _service, instance_t _instance); + protected: std::shared_ptr<serviceinfo> find_service(service_t _service, instance_t _instance) const; std::shared_ptr<serviceinfo> create_service_info(service_t _service, @@ -133,14 +132,16 @@ protected: void clear_service_info(service_t _service, instance_t _instance, bool _reliable); services_t get_services() const; + services_t get_services_remote() const; bool is_available(service_t _service, instance_t _instance, major_version_t _major); client_t find_local_client(service_t _service, instance_t _instance); std::shared_ptr<endpoint> create_local(client_t _client); std::shared_ptr<endpoint> find_or_create_local(client_t _client); - void remove_local(client_t _client); + void remove_local(client_t _client, bool _remove_uid); void remove_local(client_t _client, - const std::set<std::tuple<service_t, instance_t, eventgroup_t>>& _subscribed_eventgroups); + const std::set<std::tuple<service_t, instance_t, eventgroup_t>>& _subscribed_eventgroups, + bool _remove_uid); std::shared_ptr<endpoint> find_local(client_t _client); std::shared_ptr<endpoint> find_local(service_t _service, @@ -182,7 +183,7 @@ protected: eventgroup_t _eventgroup, event_t _event); void send_pending_notify_ones(service_t _service, instance_t _instance, - eventgroup_t _eventgroup, client_t _client); + eventgroup_t _eventgroup, client_t _client, bool _remote_subscriber = false); void unset_all_eventpayloads(service_t _service, instance_t _instance); void unset_all_eventpayloads(service_t _service, instance_t _instance, @@ -200,6 +201,23 @@ protected: std::set<std::tuple<service_t, instance_t, eventgroup_t>> get_subscriptions(const client_t _client); + + std::vector<event_t> find_events(service_t _service, instance_t _instance) const; + + bool is_response_allowed(client_t _sender, service_t _service, + instance_t _instance, method_t _method); + bool is_subscribe_to_any_event_allowed(client_t _client, + service_t _service, instance_t _instance, eventgroup_t _eventgroup); + + void set_incoming_subscription_state(client_t _client, service_t _service, instance_t _instance, + eventgroup_t _eventgroup, event_t _event, subscription_state_e _state); + + subscription_state_e get_incoming_subscription_state(client_t _client, service_t _service, instance_t _instance, + eventgroup_t _eventgroup, event_t _event); + + void erase_incoming_subscription_state(client_t _client, service_t _service, instance_t _instance, + eventgroup_t _eventgroup, event_t _event); + private: std::shared_ptr<endpoint> create_local_unlocked(client_t _client); std::shared_ptr<endpoint> find_local_unlocked(client_t _client); @@ -224,6 +242,7 @@ protected: std::mutex local_services_mutex_; std::map<service_t, std::map<instance_t, std::tuple< major_version_t, minor_version_t, client_t> > > local_services_; + std::map<service_t, std::map<instance_t, std::set<client_t> > > local_services_history_; // Eventgroups mutable std::mutex eventgroups_mutex_; @@ -236,7 +255,7 @@ protected: std::map<instance_t, std::map<event_t, std::shared_ptr<event> > > > events_; #ifdef USE_DLT - std::shared_ptr<tc::trace_connector> tc_; + std::shared_ptr<trace::connector_impl> tc_; #endif struct subscription_data_t { @@ -263,7 +282,7 @@ protected: std::set<subscription_data_t> pending_subscriptions_; services_t services_remote_; - std::mutex services_remote_mutex_; + mutable std::mutex services_remote_mutex_; private: services_t services_; @@ -276,7 +295,18 @@ private: std::map<instance_t, std::map<eventgroup_t, std::shared_ptr<message> > > > pending_notify_ones_; - std::mutex pending_notify_ones_mutex_; + std::recursive_mutex pending_notify_ones_mutex_; + + std::mutex event_registration_mutex_; + + std::map<client_t, + std::map<service_t, + std::map<instance_t, + std::map<eventgroup_t, + std::map<event_t, + subscription_state_e> > > > > incoming_subscription_state_; + std::recursive_mutex incoming_subscription_state_mutex_; + }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager_impl.hpp b/implementation/routing/include/routing_manager_impl.hpp index 2e90077..40abf73 100644 --- a/implementation/routing/include/routing_manager_impl.hpp +++ b/implementation/routing/include/routing_manager_impl.hpp @@ -84,8 +84,10 @@ public: bool send(client_t _client, std::shared_ptr<message> _message, bool _flush); - bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush, bool _reliable, bool _is_valid_crc = true); + virtual bool send(client_t _client, const byte_t *_data, uint32_t _size, + instance_t _instance, bool _flush, bool _reliable, + client_t _bound_client = VSOMEIP_ROUTING_CLIENT, + bool _is_valid_crc = true, bool _sent_from_remote = false); bool send_to(const std::shared_ptr<endpoint_definition> &_target, std::shared_ptr<message> _message, bool _flush); @@ -115,7 +117,7 @@ public: void notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, - client_t _client, bool _force, bool _flush); + client_t _client, bool _force, bool _flush, bool _remote_subscriber); void on_subscribe_nack(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, event_t _event, @@ -137,7 +139,7 @@ public: return routing_manager_base::find_or_create_local(_client); } - void remove_local(client_t _client); + void remove_local(client_t _client, bool _remove_uid); void on_stop_offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor); @@ -159,18 +161,27 @@ public: void on_error(const byte_t *_data, length_t _length, endpoint *_receiver, const boost::asio::ip::address &_remote_address, std::uint16_t _remote_port); - void on_message(const byte_t *_data, length_t _length, endpoint *_receiver, + void on_message(const byte_t *_data, length_t _size, endpoint *_receiver, const boost::asio::ip::address &_destination, client_t _bound_client, const boost::asio::ip::address &_remote_address, std::uint16_t _remote_port); bool on_message(service_t _service, instance_t _instance, - const byte_t *_data, length_t _size, bool _reliable, bool _is_valid_crc = true); + const byte_t *_data, length_t _size, bool _reliable, + client_t _bound_client, bool _is_valid_crc = true, + bool _is_from_remote = false); void on_notification(client_t _client, service_t _service, instance_t _instance, const byte_t *_data, length_t _size, bool _notify_one); void release_port(uint16_t _port, bool _reliable); + bool offer_service_remotely(service_t _service, instance_t _instance, + std::uint16_t _port, bool _reliable, + bool _magic_cookies_enabled); + bool stop_offer_service_remotely(service_t _service, instance_t _instance, + std::uint16_t _port, bool _reliable, + bool _magic_cookies_enabled); + // interface "service_discovery_host" typedef std::map<std::string, std::shared_ptr<servicegroup> > servicegroups_t; const servicegroups_t & get_servicegroups() const; @@ -210,7 +221,7 @@ public: void expire_subscriptions(const boost::asio::ip::address &_address); void expire_services(const boost::asio::ip::address &_address); - std::chrono::steady_clock::time_point expire_subscriptions(); + std::chrono::steady_clock::time_point expire_subscriptions(bool _force); bool has_identified(client_t _client, service_t _service, instance_t _instance, bool _reliable); @@ -230,11 +241,28 @@ public: eventgroup_t _eventgroup, const std::shared_ptr<endpoint_definition> &_subscriber); + void register_offer_acceptance_handler(offer_acceptance_handler_t _handler) const; + void register_reboot_notification_handler(reboot_notification_handler_t _handler) const; + void register_routing_ready_handler(routing_ready_handler_t _handler); + void register_routing_state_handler(routing_state_handler_t _handler); + void offer_acceptance_enabled(boost::asio::ip::address _address); + + void on_resend_provided_events_response(pending_remote_offer_id_t _id); + bool update_security_policy_configuration(uint32_t _uid, uint32_t _gid, ::std::shared_ptr<policy> _policy, + std::shared_ptr<payload> _payload, security_update_handler_t _handler); + bool remove_security_policy_configuration(uint32_t _uid, uint32_t _gid, security_update_handler_t _handler); + void on_security_update_response(pending_security_update_id_t _id, client_t _client); + std::set<client_t> find_local_clients(service_t _service, instance_t _instance); + bool is_subscribe_to_any_event_allowed(client_t _client, + service_t _service, instance_t _instance, eventgroup_t _eventgroup); + private: - bool deliver_message(const byte_t *_data, length_t _length, - instance_t _instance, bool _reliable, bool _is_valid_crc = true); + bool deliver_message(const byte_t *_data, length_t _size, + instance_t _instance, bool _reliable, client_t _bound_client, + bool _is_valid_crc = true, bool _is_from_remote = false); bool deliver_notification(service_t _service, instance_t _instance, - const byte_t *_data, length_t _length, bool _reliable, bool _is_valid_crc = true); + const byte_t *_data, length_t _length, bool _reliable, client_t _bound_client, + bool _is_valid_crc = true, bool _is_from_remote = false); instance_t find_instance(service_t _service, endpoint *_endpoint); @@ -273,6 +301,9 @@ private: std::set<eventgroup_t> get_subscribed_eventgroups(service_t _service, instance_t _instance); + + void clear_targets_and_pending_sub_from_eventgroups(service_t _service, instance_t _instance); + void clear_remote_subscriber(service_t _service, instance_t _instance); private: return_code_e check_error(const byte_t *_data, length_t _size, instance_t _instance); @@ -371,7 +402,31 @@ private: service_t _service, instance_t _instance, eventgroup_t _eventgroup, pending_subscription_id_t _pending_unsubscription_id); + void cleanup_server_endpoint(service_t _service, + const std::shared_ptr<endpoint>& _endpoint); + + pending_remote_offer_id_t pending_remote_offer_add(service_t _service, + instance_t _instance); + + std::pair<service_t, instance_t> pending_remote_offer_remove( + pending_remote_offer_id_t _id); + + void on_security_update_timeout( + const boost::system::error_code& _error, + pending_security_update_id_t _id, + std::shared_ptr<boost::asio::steady_timer> _timer); + + pending_security_update_id_t pending_security_update_add( + std::unordered_set<client_t> _clients); + + std::unordered_set<client_t> pending_security_update_get( + pending_security_update_id_t _id); + bool pending_security_update_remove( + pending_security_update_id_t _id, client_t _client); + + bool is_pending_security_update_finished( + pending_security_update_id_t _id); std::shared_ptr<routing_manager_stub> stub_; std::shared_ptr<sd::service_discovery> discovery_; @@ -454,14 +509,34 @@ private: std::map<std::tuple<service_t, instance_t, eventgroup_t, client_t>, subscription_state_e> remote_subscription_state_; - std::map<e2exf::data_identifier, std::shared_ptr<e2e::profile_interface::protector>> custom_protectors; - std::map<e2exf::data_identifier, std::shared_ptr<e2e::profile_interface::checker>> custom_checkers; + std::map<e2exf::data_identifier_t, std::shared_ptr<e2e::profile_interface::protector>> custom_protectors; + std::map<e2exf::data_identifier_t, std::shared_ptr<e2e::profile_interface::checker>> custom_checkers; std::mutex status_log_timer_mutex_; boost::asio::steady_timer status_log_timer_; std::mutex memory_log_timer_mutex_; boost::asio::steady_timer memory_log_timer_; + + routing_ready_handler_t routing_ready_handler_; + routing_state_handler_t routing_state_handler_; + + std::mutex pending_remote_offers_mutex_; + pending_remote_offer_id_t pending_remote_offer_id_; + std::map<pending_remote_offer_id_t, std::pair<service_t, instance_t>> pending_remote_offers_; + + std::mutex last_resume_mutex_; + std::chrono::steady_clock::time_point last_resume_; + + std::mutex pending_security_updates_mutex_; + pending_security_update_id_t pending_security_update_id_; + std::map<pending_security_update_id_t, std::unordered_set<client_t>> pending_security_updates_; + + std::recursive_mutex security_update_handlers_mutex_; + std::map<pending_security_update_id_t, security_update_handler_t> security_update_handlers_; + + std::mutex security_update_timers_mutex_; + std::map<pending_security_update_id_t, std::shared_ptr<boost::asio::steady_timer>> security_update_timers_; }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager_proxy.hpp b/implementation/routing/include/routing_manager_proxy.hpp index 94d290c..fbe775d 100644 --- a/implementation/routing/include/routing_manager_proxy.hpp +++ b/implementation/routing/include/routing_manager_proxy.hpp @@ -61,7 +61,9 @@ public: eventgroup_t _eventgroup, event_t _event); bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush = true, bool _reliable = false, bool _is_valid_crc= true); + instance_t _instance, bool _flush, bool _reliable, + client_t _bound_client = VSOMEIP_ROUTING_CLIENT, + bool _is_valid_crc = true, bool _sent_from_remote = false); bool send_to(const std::shared_ptr<endpoint_definition> &_target, std::shared_ptr<message> _message, bool _flush); @@ -82,7 +84,7 @@ public: void on_connect(std::shared_ptr<endpoint> _endpoint); void on_disconnect(std::shared_ptr<endpoint> _endpoint); - void on_message(const byte_t *_data, length_t _length, endpoint *_receiver, + void on_message(const byte_t *_data, length_t _size, endpoint *_receiver, const boost::asio::ip::address &_destination, client_t _bound_client, const boost::asio::ip::address &_remote_address, @@ -119,7 +121,7 @@ private: instance_t _instance); void send_register_event(client_t _client, service_t _service, instance_t _instance, event_t _event, - const std::set<eventgroup_t> &_eventgroup, + const std::set<eventgroup_t> &_eventgroups, bool _is_field, bool _is_provided); void send_subscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, @@ -183,6 +185,13 @@ private: eventgroup_t _eventgroup, pending_subscription_id_t _subscription_id); + void resend_provided_event_registrations(); + void send_resend_provided_event_response(pending_remote_offer_id_t _id); + + void send_update_security_policy_response(pending_security_update_id_t _update_id); + void send_remove_security_policy_response(pending_security_update_id_t _update_id); + void on_update_security_credentials(const byte_t *_data, uint32_t _size); + private: enum class inner_state_type_e : std::uint8_t { ST_REGISTERED = 0x0, diff --git a/implementation/routing/include/routing_manager_stub.hpp b/implementation/routing/include/routing_manager_stub.hpp index f97aab5..d586518 100644 --- a/implementation/routing/include/routing_manager_stub.hpp +++ b/implementation/routing/include/routing_manager_stub.hpp @@ -14,6 +14,7 @@ #include <set> #include <thread> #include <atomic> +#include <unordered_set> #include <boost/asio/io_service.hpp> #include <boost/asio/steady_timer.hpp> @@ -43,7 +44,7 @@ public: void on_connect(std::shared_ptr<endpoint> _endpoint); void on_disconnect(std::shared_ptr<endpoint> _endpoint); - void on_message(const byte_t *_data, length_t _length, endpoint *_receiver, + void on_message(const byte_t *_data, length_t _size, endpoint *_receiver, const boost::asio::ip::address &_destination, client_t _bound_client, const boost::asio::ip::address &_remote_address, @@ -82,6 +83,7 @@ public: bool send_ping(client_t _client); bool is_registered(client_t _client) const; client_t get_client() const; + void handle_credentials(const client_t _client, std::set<service_data_t>& _requests); void handle_requests(const client_t _client, std::set<service_data_t>& _requests); void send_identify_request_command(std::shared_ptr<vsomeip::endpoint> _target, @@ -95,12 +97,31 @@ public: void update_registration(client_t _client, registration_type_e _type); void print_endpoint_status() const; + + bool send_provided_event_resend_request(client_t _client, + pending_remote_offer_id_t _id); + + bool is_policy_cached(uint32_t _uid); + + void policy_cache_add(uint32_t _uid, std::shared_ptr<payload> _payload); + + void policy_cache_remove(uint32_t _uid); + + bool send_update_security_policy_request(client_t _client, pending_security_update_id_t _update_id, + uint32_t _uid, std::shared_ptr<payload> _payload); + bool send_remove_security_policy_request(client_t _client, pending_security_update_id_t _update_id, + uint32_t _uid, uint32_t _gid); + + bool send_cached_security_policies(client_t _client); + private: void broadcast(const std::vector<byte_t> &_command) const; void on_register_application(client_t _client); void on_deregister_application(client_t _client); + void distribute_credentials(client_t _hoster, service_t _service, instance_t _instance); + void inform_requesters(client_t _hoster, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor, routing_info_entry_e _entry, @@ -138,6 +159,10 @@ private: minor_version_t _minor); void send_offered_services_info(const client_t _target); + void create_client_credentials_info(const client_t _target); + void insert_client_credentials_info(client_t _target, std::set<std::pair<uint32_t, uint32_t>> _credentials); + void send_client_credentials_info(const client_t _target); + void on_client_id_timer_expired(boost::system::error_code const &_error); private: @@ -182,6 +207,12 @@ private: std::map<client_t, std::vector<byte_t>> client_routing_info_; std::map<client_t, std::vector<byte_t>> offered_services_info_; + std::map<client_t, std::vector<byte_t>> client_credentials_info_; + + + std::mutex updated_security_policies_mutex_; + std::map<uint32_t, std::shared_ptr<payload>> updated_security_policies_; + }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager_stub_host.hpp b/implementation/routing/include/routing_manager_stub_host.hpp index a6d461e..5c474fb 100644 --- a/implementation/routing/include/routing_manager_stub_host.hpp +++ b/implementation/routing/include/routing_manager_stub_host.hpp @@ -61,7 +61,8 @@ public: pending_subscription_id_t _unsubscription_id) = 0; virtual bool on_message(service_t _service, instance_t _instance, - const byte_t *_data, length_t _size, bool _reliable, bool _is_valid_crc = true) = 0; + const byte_t *_data, length_t _size, bool _reliable, client_t _bound_client, + bool _is_valid_crc = true, bool _is_from_remote = false) = 0; virtual void on_notification(client_t _client, service_t _service, instance_t _instance, @@ -78,7 +79,7 @@ public: virtual std::shared_ptr<endpoint> find_or_create_local( client_t _client) = 0; - virtual void remove_local(client_t _client) = 0; + virtual void remove_local(client_t _client, bool _remove_local) = 0; virtual boost::asio::io_service & get_io() = 0; virtual client_t get_client() const = 0; @@ -91,6 +92,15 @@ public: virtual void handle_client_error(client_t _client) = 0; virtual void set_routing_state(routing_state_e _routing_state) = 0; + + virtual void on_resend_provided_events_response(pending_remote_offer_id_t _id) = 0; + + virtual void on_security_update_response(pending_security_update_id_t _id, client_t _client) = 0; + + virtual std::set<client_t> find_local_clients(service_t _service, instance_t _instance) = 0; + + virtual bool is_subscribe_to_any_event_allowed(client_t _client, + service_t _service, instance_t _instance, eventgroup_t _eventgroup) = 0; }; } // namespace vsomeip diff --git a/implementation/routing/include/serviceinfo.hpp b/implementation/routing/include/serviceinfo.hpp index 7eab1ec..72be585 100644 --- a/implementation/routing/include/serviceinfo.hpp +++ b/implementation/routing/include/serviceinfo.hpp @@ -24,6 +24,7 @@ class serviceinfo { public: VSOMEIP_EXPORT serviceinfo(major_version_t _major, minor_version_t _minor, ttl_t _ttl, bool _is_local); + VSOMEIP_EXPORT serviceinfo(const serviceinfo& _other); VSOMEIP_EXPORT ~serviceinfo(); VSOMEIP_EXPORT servicegroup * get_group() const; @@ -36,7 +37,7 @@ public: VSOMEIP_EXPORT void set_ttl(ttl_t _ttl); VSOMEIP_EXPORT std::chrono::milliseconds get_precise_ttl() const; - VSOMEIP_EXPORT void set_precise_ttl(std::chrono::milliseconds _ttl); + VSOMEIP_EXPORT void set_precise_ttl(std::chrono::milliseconds _precise_ttl); VSOMEIP_EXPORT std::shared_ptr<endpoint> get_endpoint(bool _reliable) const; VSOMEIP_EXPORT void set_endpoint(std::shared_ptr<endpoint> _endpoint, diff --git a/implementation/routing/include/types.hpp b/implementation/routing/include/types.hpp index f33a944..59acfa3 100644 --- a/implementation/routing/include/types.hpp +++ b/implementation/routing/include/types.hpp @@ -118,6 +118,8 @@ enum remote_subscription_state_e : std::uint8_t { SUBSCRIPTION_ERROR }; +typedef std::uint32_t pending_remote_offer_id_t; + } // namespace vsomeip diff --git a/implementation/routing/src/event.cpp b/implementation/routing/src/event.cpp index 13cd36e..ce78a65 100644 --- a/implementation/routing/src/event.cpp +++ b/implementation/routing/src/event.cpp @@ -138,7 +138,7 @@ void event::set_payload(const std::shared_ptr<payload> &_payload, client_t _clie if (set_payload_helper(_payload, _force)) { reset_payload(_payload); if (is_updating_on_change_) { - notify_one(_client, _flush); + notify_one_unlocked(_client, _flush); } } } else { @@ -155,7 +155,7 @@ void event::set_payload(const std::shared_ptr<payload> &_payload, if (set_payload_helper(_payload, _force)) { reset_payload(_payload); if (is_updating_on_change_) { - notify_one(_target, _flush); + notify_one_unlocked(_target, _flush); } } } else { @@ -244,7 +244,7 @@ void event::update_cbk(boost::system::error_code const &_error) { std::lock_guard<std::mutex> its_lock(mutex_); cycle_timer_.expires_from_now(cycle_); notify(true); - std::function<void(boost::system::error_code const &)> its_handler = + auto its_handler = std::bind(&event::update_cbk, shared_from_this(), std::placeholders::_1); cycle_timer_.async_wait(its_handler); @@ -261,6 +261,11 @@ void event::notify(bool _flush) { } void event::notify_one(const std::shared_ptr<endpoint_definition> &_target, bool _flush) { + std::lock_guard<std::mutex> its_lock(mutex_); + notify_one_unlocked(_target, _flush); +} + +void event::notify_one_unlocked(const std::shared_ptr<endpoint_definition> &_target, bool _flush) { if (is_set_) { routing_->send_to(_target, message_, _flush); } else { @@ -270,6 +275,11 @@ void event::notify_one(const std::shared_ptr<endpoint_definition> &_target, bool } void event::notify_one(client_t _client, bool _flush) { + std::lock_guard<std::mutex> its_lock(mutex_); + notify_one_unlocked(_client, _flush); +} + +void event::notify_one_unlocked(client_t _client, bool _flush) { if (is_set_) { routing_->send(_client, message_, _flush); } else { @@ -421,7 +431,7 @@ void event::set_cache_placeholder(bool _is_cache_place_holder) { void event::start_cycle() { if (std::chrono::milliseconds::zero() != cycle_) { cycle_timer_.expires_from_now(cycle_); - std::function<void(boost::system::error_code const &)> its_handler = + auto its_handler = std::bind(&event::update_cbk, shared_from_this(), std::placeholders::_1); cycle_timer_.async_wait(its_handler); diff --git a/implementation/routing/src/routing_manager_base.cpp b/implementation/routing/src/routing_manager_base.cpp index 62ae6f2..517ccdb 100644 --- a/implementation/routing/src/routing_manager_base.cpp +++ b/implementation/routing/src/routing_manager_base.cpp @@ -13,6 +13,9 @@ #include "../../logging/include/logger.hpp" #include "../../endpoints/include/local_client_endpoint_impl.hpp" #include "../../endpoints/include/local_server_endpoint_impl.hpp" +#ifdef USE_DLT +#include "../../tracing/include/connector_impl.hpp" +#endif namespace vsomeip { @@ -25,7 +28,7 @@ routing_manager_base::routing_manager_base(routing_manager_host *_host) : std::make_shared<serializer>( configuration_->get_buffer_shrink_threshold())) #ifdef USE_DLT - , tc_(tc::trace_connector::get()) + , tc_(trace::connector_impl::get()) #endif { const uint32_t its_buffer_shrink_threshold = @@ -153,6 +156,16 @@ void routing_manager_base::release_service(client_t _client, service_t _service, if (its_info) { its_info->remove_client(_client); } + { + std::lock_guard<std::mutex> its_lock(local_services_mutex_); + auto found_service = local_services_history_.find(_service); + if (found_service != local_services_history_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + local_services_history_.erase(_service); + } + } + } } void routing_manager_base::register_event(client_t _client, service_t _service, instance_t _instance, @@ -160,6 +173,7 @@ void routing_manager_base::register_event(client_t _client, service_t _service, std::chrono::milliseconds _cycle, bool _change_resets_cycle, epsilon_change_func_t _epsilon_change_func, bool _is_provided, bool _is_shadow, bool _is_cache_placeholder) { + std::lock_guard<std::mutex> its_event_reg_lock(event_registration_mutex_); std::shared_ptr<event> its_event = find_event(_service, _instance, _event); bool transfer_subscriptions_from_any_event(false); if (its_event) { @@ -438,6 +452,83 @@ std::set<std::shared_ptr<event>> routing_manager_base::find_events( return (its_events); } +std::vector<event_t> routing_manager_base::find_events( + service_t _service, instance_t _instance) const { + std::vector<event_t> its_events; + std::lock_guard<std::mutex> its_lock(events_mutex_); + const auto found_service = events_.find(_service); + if (found_service != events_.end()) { + const auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + for (const auto& e : found_instance->second) { + its_events.push_back(e.first); + } + } + } + return (its_events); +} + +bool routing_manager_base::is_response_allowed(client_t _sender, service_t _service, + instance_t _instance, method_t _method) { + if (!configuration_->is_security_enabled()) { + return true; + } + + if (_sender == find_local_client(_service, _instance)) { + // sender is still offering the service + return true; + } + + std::lock_guard<std::mutex> its_lock(local_services_mutex_); + auto found_service = local_services_history_.find(_service); + if (found_service != local_services_history_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + auto found_client = found_instance->second.find(_sender); + if (found_client != found_instance->second.end()) { + // sender was offering the service and is still connected + return true; + } + } + } + + // service is now offered by another client + // or service is not offered at all + std::string security_mode_text = "!"; + if (!configuration_->is_audit_mode_enabled()) { + security_mode_text = ", but will be allowed due to audit mode is active!"; + } + + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_base::is_response_allowed: " + << "received a response from client 0x" << _sender + << " which does not offer service/instance/method " + << _service << "/" << _instance << "/" << _method + << security_mode_text; + + return !configuration_->is_audit_mode_enabled(); +} + +bool routing_manager_base::is_subscribe_to_any_event_allowed(client_t _client, + service_t _service, instance_t _instance, eventgroup_t _eventgroup) { + bool is_allowed(true); + auto its_eventgroup = find_eventgroup(_service, _instance, _eventgroup); + if (its_eventgroup) { + for (auto e : its_eventgroup->get_events()) { + if (!configuration_->is_client_allowed(_client, _service, _instance, e->get_event())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex + << _client << " : routing_manager_base::is_subscribe_to_any_event_allowed: " + << "subscribes to service/instance/event " + << _service << "/" << _instance << "/" << e->get_event() + << " which violates the security policy!"; + is_allowed = false; + break; + } + } + } + return is_allowed; +} + void routing_manager_base::subscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, major_version_t _major, event_t _event, @@ -486,13 +577,14 @@ void routing_manager_base::notify(service_t _service, instance_t _instance, void routing_manager_base::notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, - client_t _client, bool _force, bool _flush) { + client_t _client, bool _force, bool _flush, bool _remote_subscriber) { std::shared_ptr<event> its_event = find_event(_service, _instance, _event); if (its_event) { // Event is valid for service/instance bool found_eventgroup(false); bool already_subscribed(false); eventgroup_t valid_group = 0; + subscription_state_e its_subscription_state(subscription_state_e::SUBSCRIPTION_NOT_ACKNOWLEDGED); // Iterate over all groups of the event to ensure at least // one valid eventgroup for service/instance exists. for (auto its_group : its_event->get_eventgroups()) { @@ -501,10 +593,13 @@ void routing_manager_base::notify_one(service_t _service, instance_t _instance, // Eventgroup is valid for service/instance found_eventgroup = true; valid_group = its_group; + its_subscription_state = get_incoming_subscription_state(_client, _service, + _instance, valid_group, _event); if (find_local(_client)) { already_subscribed = its_event->has_subscriber(its_group, _client); - } else { - // Remotes always needs to be marked as subscribed here + } else if (subscription_state_e::IS_SUBSCRIBING != its_subscription_state + || _remote_subscriber) { + // Remotes always needs to be marked as subscribed here if they are not currently subscribing already_subscribed = true; } break; @@ -514,19 +609,29 @@ void routing_manager_base::notify_one(service_t _service, instance_t _instance, if (already_subscribed) { its_event->set_payload(_payload, _client, _force, _flush); } else { - std::shared_ptr<message> its_notification - = runtime::get()->create_notification(); - its_notification->set_service(_service); - its_notification->set_instance(_instance); - its_notification->set_method(_event); - its_notification->set_payload(_payload); - auto service_info = find_service(_service, _instance); - if (service_info) { - its_notification->set_interface_version(service_info->get_major()); - } - { - std::lock_guard<std::mutex> its_lock(pending_notify_ones_mutex_); - pending_notify_ones_[_service][_instance][valid_group] = its_notification; + // cache notification if subscription is in progress + if (subscription_state_e::IS_SUBSCRIBING == its_subscription_state) { + VSOMEIP_INFO << "routing_manager_base::notify_one(" + << std::hex << std::setw(4) << std::setfill('0') << _client << "): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance << "." + << std::hex << std::setw(4) << std::setfill('0') << valid_group << "." + << std::hex << std::setw(4) << std::setfill('0') << _event << "]" + << " insert pending notification!"; + std::shared_ptr<message> its_notification + = runtime::get()->create_notification(); + its_notification->set_service(_service); + its_notification->set_instance(_instance); + its_notification->set_method(_event); + its_notification->set_payload(_payload); + auto service_info = find_service(_service, _instance); + if (service_info) { + its_notification->set_interface_version(service_info->get_major()); + } + { + std::lock_guard<std::recursive_mutex> its_lock(pending_notify_ones_mutex_); + pending_notify_ones_[_service][_instance][valid_group] = its_notification; + } } } } @@ -538,16 +643,23 @@ void routing_manager_base::notify_one(service_t _service, instance_t _instance, } void routing_manager_base::send_pending_notify_ones(service_t _service, instance_t _instance, - eventgroup_t _eventgroup, client_t _client) { - std::lock_guard<std::mutex> its_lock(pending_notify_ones_mutex_); + eventgroup_t _eventgroup, client_t _client, bool _remote_subscriber) { + std::lock_guard<std::recursive_mutex> its_lock(pending_notify_ones_mutex_); auto its_service = pending_notify_ones_.find(_service); if (its_service != pending_notify_ones_.end()) { auto its_instance = its_service->second.find(_instance); if (its_instance != its_service->second.end()) { auto its_group = its_instance->second.find(_eventgroup); if (its_group != its_instance->second.end()) { + VSOMEIP_INFO << "routing_manager_base::send_pending_notify_ones(" + << std::hex << std::setw(4) << std::setfill('0') << _client << "): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance << "." + << std::hex << std::setw(4) << std::setfill('0') << _eventgroup << "." + << std::hex << std::setw(4) << std::setfill('0') << its_group->second->get_method() << "]"; + notify_one(_service, _instance, its_group->second->get_method(), - its_group->second->get_payload(), _client, false, true); + its_group->second->get_payload(), _client, false, true, _remote_subscriber); its_instance->second.erase(_eventgroup); } } @@ -623,18 +735,18 @@ void routing_manager_base::notify_one_current_value( } } -bool routing_manager_base::send(client_t its_client, +bool routing_manager_base::send(client_t _client, std::shared_ptr<message> _message, bool _flush) { bool is_sent(false); if (utility::is_request(_message->get_message_type())) { - _message->set_client(its_client); + _message->set_client(_client); } std::lock_guard<std::mutex> its_lock(serialize_mutex_); if (serializer_->serialize(_message.get())) { - is_sent = send(its_client, serializer_->get_data(), + is_sent = send(_client, serializer_->get_data(), serializer_->get_size(), _message->get_instance(), - _flush, _message->is_reliable()); + _flush, _message->is_reliable(), get_client(), true, false); serializer_->reset(); } else { VSOMEIP_ERROR << "Failed to serialize message. Check message size!"; @@ -715,6 +827,11 @@ services_t routing_manager_base::get_services() const { return services_; } +services_t routing_manager_base::get_services_remote() const { + std::lock_guard<std::mutex> its_lock(services_remote_mutex_); + return services_remote_; +} + bool routing_manager_base::is_available(service_t _service, instance_t _instance, major_version_t _major) { bool available(false); @@ -737,6 +854,25 @@ bool routing_manager_base::is_available(service_t _service, instance_t _instance return available; } +std::set<client_t> routing_manager_base::find_local_clients(service_t _service, instance_t _instance) { + std::set<client_t> its_clients; + std::lock_guard<std::mutex> its_lock(local_services_mutex_); + auto its_service = local_services_.find(_service); + if (its_service != local_services_.end()) { + if (_instance == ANY_INSTANCE) { + for (auto its_instance : its_service->second) { + its_clients.insert(std::get<2>(its_instance.second)); + } + } else { + auto its_instance = its_service->second.find(_instance); + if (its_instance != its_service->second.end()) { + its_clients.insert(std::get<2>(its_instance->second)); + } + } + } + return its_clients; +} + client_t routing_manager_base::find_local_client(service_t _service, instance_t _instance) { std::lock_guard<std::mutex> its_lock(local_services_mutex_); client_t its_client(VSOMEIP_ROUTING_CLIENT); @@ -814,12 +950,16 @@ std::shared_ptr<endpoint> routing_manager_base::find_or_create_local(client_t _c return (its_endpoint); } -void routing_manager_base::remove_local(client_t _client) { - remove_local(_client, get_subscriptions(_client)); +void routing_manager_base::remove_local(client_t _client, bool _remove_uid) { + remove_local(_client, get_subscriptions(_client), _remove_uid); } void routing_manager_base::remove_local(client_t _client, - const std::set<std::tuple<service_t, instance_t, eventgroup_t>>& _subscribed_eventgroups) { + const std::set<std::tuple<service_t, instance_t, eventgroup_t>>& _subscribed_eventgroups, + bool _remove_uid) { + if (_remove_uid) { + configuration_->remove_client_to_uid_gid_mapping(_client); + } for (auto its_subscription : _subscribed_eventgroups) { host_->on_subscription(std::get<0>(its_subscription), std::get<1>(its_subscription), std::get<2>(its_subscription), _client, false, [](const bool _subscription_accepted){ (void)_subscription_accepted; }); @@ -854,6 +994,25 @@ void routing_manager_base::remove_local(client_t _client, if (local_services_[si.first].size() == 0) local_services_.erase(si.first); } + + // remove disconnected client from offer service history + std::set<std::tuple<service_t, instance_t, client_t>> its_clients; + for (auto& s : local_services_history_) { + for (auto& i : s.second) { + for (auto& c : i.second) { + if (c == _client) { + its_clients.insert(std::make_tuple(s.first, i.first, c)); + } + } + } + } + + for (auto& sic : its_clients) { + local_services_history_[std::get<0>(sic)][std::get<1>(sic)].erase(std::get<2>(sic)); + if (local_services_history_[std::get<0>(sic)][std::get<1>(sic)].size() == 0) { + local_services_history_.erase(std::get<0>(sic)); + } + } } } @@ -985,7 +1144,7 @@ bool routing_manager_base::send_local_notification(client_t _client, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(nullptr, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -1202,4 +1361,71 @@ routing_manager_base::get_local_endpoints() { return local_endpoints_; } +void routing_manager_base::set_incoming_subscription_state(client_t _client, service_t _service, instance_t _instance, + eventgroup_t _eventgroup, event_t _event, subscription_state_e _state) { + std::lock_guard<std::recursive_mutex> its_lock(incoming_subscription_state_mutex_); + incoming_subscription_state_[_client][_service][_instance][_eventgroup][_event] = _state; +} + +subscription_state_e routing_manager_base::get_incoming_subscription_state(client_t _client, + service_t _service, instance_t _instance, + eventgroup_t _eventgroup, event_t _event) { + std::lock_guard<std::recursive_mutex> its_lock(incoming_subscription_state_mutex_); + const auto its_client = incoming_subscription_state_.find(_client); + if (its_client != incoming_subscription_state_.end()) { + const auto its_service = its_client->second.find(_service); + if (its_service != its_client->second.end()) { + const auto its_instance = its_service->second.find(_instance); + if (its_instance != its_service->second.end()) { + const auto its_group = its_instance->second.find(_eventgroup); + if (its_group != its_instance->second.end()) { + const auto its_event = its_group->second.find(_event); + if (its_event != its_group->second.end()) { + return its_event->second; + } + // If a specific event was not found, check if there is a remote subscriber to ANY_EVENT + const auto its_any_event = its_group->second.find(ANY_EVENT); + if (its_any_event != its_group->second.end()) { + return its_any_event->second; + } + } + } + } + } + return subscription_state_e::SUBSCRIPTION_NOT_ACKNOWLEDGED; +} + +void routing_manager_base::erase_incoming_subscription_state(client_t _client, service_t _service, instance_t _instance, + eventgroup_t _eventgroup, event_t _event) { + std::lock_guard<std::recursive_mutex> its_lock(incoming_subscription_state_mutex_); + const auto its_client = incoming_subscription_state_.find(_client); + if (its_client != incoming_subscription_state_.end()) { + const auto its_service = its_client->second.find(_service); + if (its_service != its_client->second.end()) { + const auto its_instance = its_service->second.find(_instance); + if (its_instance != its_service->second.end()) { + const auto its_group = its_instance->second.find(_eventgroup); + if (its_group != its_instance->second.end()) { + const auto its_event = its_group->second.find(_event); + if (its_event != its_group->second.end()) { + its_group->second.erase(_event); + if (its_group->second.empty()) { + its_instance->second.erase(its_group); + if (its_instance->second.empty()) { + its_service->second.erase(its_instance); + if (its_service->second.empty()) { + its_client->second.erase(its_service); + if (its_client->second.empty()) { + incoming_subscription_state_.erase(its_client); + } + } + } + } + } + } + } + } + } +} + } // namespace vsomeip diff --git a/implementation/routing/src/routing_manager_impl.cpp b/implementation/routing/src/routing_manager_impl.cpp index faf5282..f8dbd14 100644 --- a/implementation/routing/src/routing_manager_impl.cpp +++ b/implementation/routing/src/routing_manager_impl.cpp @@ -51,6 +51,9 @@ #include "../../utility/include/byteorder.hpp" #include "../../utility/include/utility.hpp" #include "../../plugin/include/plugin_manager.hpp" +#ifdef USE_DLT +#include "../../tracing/include/connector_impl.hpp" +#endif #include "../../e2e_protection/include/buffer/buffer.hpp" #include "../../e2e_protection/include/e2exf/config.hpp" @@ -75,7 +78,10 @@ routing_manager_impl::routing_manager_impl(routing_manager_host *_host) : watchdog_timer_(_host->get_io()), #endif status_log_timer_(_host->get_io()), - memory_log_timer_(_host->get_io()) + memory_log_timer_(_host->get_io()), + pending_remote_offer_id_(0), + last_resume_(std::chrono::steady_clock::now().min()), + pending_security_update_id_(0) { } @@ -90,6 +96,16 @@ client_t routing_manager_impl::get_client() const { return routing_manager_base::get_client(); } +std::set<client_t> routing_manager_impl::find_local_clients(service_t _service, instance_t _instance) { + return routing_manager_base::find_local_clients(_service, _instance); +} + +bool routing_manager_impl::is_subscribe_to_any_event_allowed(client_t _client, + service_t _service, instance_t _instance, eventgroup_t _eventgroup) { + return routing_manager_base::is_subscribe_to_any_event_allowed(_client, + _service, _instance, _eventgroup); +} + void routing_manager_impl::init() { routing_manager_base::init(); @@ -106,7 +122,7 @@ void routing_manager_impl::init() { plugin_type_e::SD_RUNTIME_PLUGIN, VSOMEIP_SD_LIBRARY); if (its_plugin) { VSOMEIP_INFO << "Service Discovery module loaded."; - discovery_ = std::dynamic_pointer_cast<sd::runtime>(its_plugin)->create_service_discovery(this); + discovery_ = std::dynamic_pointer_cast<sd::runtime>(its_plugin)->create_service_discovery(this, configuration_); discovery_->init(); } else { VSOMEIP_ERROR << "Service Discovery module could not be loaded!"; @@ -116,11 +132,11 @@ void routing_manager_impl::init() { if( configuration_->is_e2e_enabled()) { VSOMEIP_INFO << "E2E protection enabled."; - std::map<e2exf::data_identifier, std::shared_ptr<cfg::e2e>> its_e2e_configuration = configuration_->get_e2e_configuration(); + std::map<e2exf::data_identifier_t, std::shared_ptr<cfg::e2e>> its_e2e_configuration = configuration_->get_e2e_configuration(); for (auto &identifier : its_e2e_configuration) { auto its_cfg = identifier.second; if(its_cfg->profile == "CRC8") { - e2exf::data_identifier its_data_identifier = {its_cfg->service_id, its_cfg->event_id}; + e2exf::data_identifier_t its_data_identifier = {its_cfg->service_id, its_cfg->event_id}; e2e::profile01::profile_config its_profile_config = e2e::profile01::profile_config(its_cfg->crc_offset, its_cfg->data_id, (e2e::profile01::p01_data_id_mode) its_cfg->data_id_mode, its_cfg->data_length, its_cfg->counter_offset, its_cfg->data_id_nibble_offset); if ((its_cfg->variant == "protector") || (its_cfg->variant == "both")) { @@ -130,7 +146,7 @@ void routing_manager_impl::init() { custom_checkers[its_data_identifier] = std::make_shared<e2e::profile01::profile_01_checker>(its_profile_config); } } else if(its_cfg->profile == "CRC32") { - e2exf::data_identifier its_data_identifier = {its_cfg->service_id, its_cfg->event_id}; + e2exf::data_identifier_t its_data_identifier = {its_cfg->service_id, its_cfg->event_id}; e2e::profile_custom::profile_config its_profile_config = e2e::profile_custom::profile_config(its_cfg->crc_offset); if ((its_cfg->variant == "protector") || (its_cfg->variant == "both")) { @@ -237,7 +253,7 @@ void routing_manager_impl::stop() { for (auto client: get_connected_clients()) { if (client != VSOMEIP_ROUTING_CLIENT) { - remove_local(client); + remove_local(client, true); } } } @@ -296,22 +312,35 @@ void routing_manager_impl::stop_offer_service(client_t _client, << std::hex << std::setw(4) << std::setfill('0') << _service << "." << std::hex << std::setw(4) << std::setfill('0') << _instance << ":" << std::dec << int(_major) << "." << _minor << "]"; - + bool is_local(false); { - std::lock_guard<std::mutex> its_lock(pending_sd_offers_mutex_); - for (auto it = pending_sd_offers_.begin(); it != pending_sd_offers_.end(); ) { - if (it->first == _service && it->second == _instance) { - it = pending_sd_offers_.erase(it); - break; - } else { - ++it; + std::shared_ptr<serviceinfo> its_info = find_service(_service, _instance); + is_local = (its_info && its_info->is_local()); + } + if (is_local) { + { + std::lock_guard<std::mutex> its_lock(pending_sd_offers_mutex_); + for (auto it = pending_sd_offers_.begin(); it != pending_sd_offers_.end(); ) { + if (it->first == _service && it->second == _instance) { + it = pending_sd_offers_.erase(it); + break; + } else { + ++it; + } } } - } - on_stop_offer_service(_client, _service, _instance, _major, _minor); - stub_->on_stop_offer_service(_client, _service, _instance, _major, _minor); - on_availability(_service, _instance, false, _major, _minor); + on_stop_offer_service(_client, _service, _instance, _major, _minor); + stub_->on_stop_offer_service(_client, _service, _instance, _major, _minor); + on_availability(_service, _instance, false, _major, _minor); + } else { + VSOMEIP_WARNING << __func__ << " received STOP_OFFER(" + << std::hex << std::setw(4) << std::setfill('0') << _client <<"): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << ":" << std::dec << int(_major) << "." << _minor << "] " + << "for remote service --> ignore"; + } } void routing_manager_impl::request_service(client_t _client, service_t _service, @@ -435,6 +464,8 @@ void routing_manager_impl::subscribe(client_t _client, service_t _service, << std::dec << (uint16_t)_major << "]"; const client_t its_local_client = find_local_client(_service, _instance); if (get_client() == its_local_client) { + routing_manager_base::set_incoming_subscription_state(_client, _service, _instance, + _eventgroup, _event, subscription_state_e::IS_SUBSCRIBING); auto self = shared_from_this(); host_->on_subscription(_service, _instance, _eventgroup, _client, true, [this, self, _client, _service, _instance, _eventgroup, @@ -452,6 +483,8 @@ void routing_manager_impl::subscribe(client_t _client, service_t _service, } routing_manager_base::subscribe(_client, _service, _instance, _eventgroup, _major, _event, _subscription_type); send_pending_notify_ones(_service, _instance, _eventgroup, _client); + routing_manager_base::erase_incoming_subscription_state(_client, _service, _instance, + _eventgroup, _event); }); } else { if (discovery_) { @@ -581,8 +614,8 @@ bool routing_manager_impl::send(client_t _client, } bool routing_manager_impl::send(client_t _client, const byte_t *_data, - length_t _size, instance_t _instance, - bool _flush, bool _reliable, bool _is_valid_crc) { + length_t _size, instance_t _instance, bool _flush, bool _reliable, + client_t _bound_client, bool _is_valid_crc, bool _sent_from_remote) { bool is_sent(false); if (_size > VSOMEIP_MESSAGE_TYPE_POS) { std::shared_ptr<endpoint> its_target; @@ -616,12 +649,12 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(its_target, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); #endif - deliver_message(_data, _size, _instance, _reliable, _is_valid_crc); + deliver_message(_data, _size, _instance, _reliable, _bound_client, _is_valid_crc, _sent_from_remote); return true; } its_target = find_local(_client); @@ -634,7 +667,7 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(its_target, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -648,7 +681,7 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, || (find_local_client(its_service, _instance) == host_->get_client() && is_request)) { // TODO: find out how to handle session id here - is_sent = deliver_message(_data, _size, _instance, _reliable, _is_valid_crc); + is_sent = deliver_message(_data, _size, _instance, _reliable, _bound_client, _is_valid_crc, _sent_from_remote); } else { e2e_buffer outputBuffer; if( configuration_->is_e2e_enabled()) { @@ -675,7 +708,7 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(its_target, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -754,7 +787,7 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(nullptr, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -786,7 +819,7 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(its_target, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -869,7 +902,7 @@ bool routing_manager_impl::send_to( const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(its_endpoint, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -892,7 +925,7 @@ bool routing_manager_impl::send_to( const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(its_endpoint, true, 0x0)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -911,8 +944,10 @@ void routing_manager_impl::register_event( bool _is_provided, bool _is_shadow, bool _is_cache_placeholder) { auto its_event = find_event(_service, _instance, _event); bool is_first(false); - if (its_event && !its_event->has_ref(_client, _is_provided)) { - is_first = true; + if (its_event) { + if (!its_event->has_ref(_client, _is_provided)) { + is_first = true; + } } else { is_first = true; } @@ -950,10 +985,10 @@ void routing_manager_impl::unregister_shadow_event(client_t _client, void routing_manager_impl::notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, client_t _client, - bool _force, bool _flush) { + bool _force, bool _flush, bool _remote_subscriber) { if (find_local(_client)) { routing_manager_base::notify_one(_service, _instance, _event, _payload, - _client, _force, _flush); + _client, _force, _flush, _remote_subscriber); } else { std::shared_ptr<event> its_event = find_event(_service, _instance, _event); if (its_event) { @@ -1034,6 +1069,113 @@ void routing_manager_impl::release_port(uint16_t _port, bool _reliable) { used_client_ports_[_reliable].erase(_port); } +bool routing_manager_impl::offer_service_remotely(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled) { + bool ret = true; + + if(!is_available(_service, _instance, ANY_MAJOR)) { + VSOMEIP_ERROR << __func__ << ": Service [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << "] is not offered locally! Won't offer it remotely."; + ret = false; + } else { + // update service info in configuration + if (!configuration_->remote_offer_info_add(_service, _instance, _port, + _reliable, _magic_cookies_enabled)) { + ret = false; + } else { + // trigger event registration again to create shadow events + const client_t its_offering_client = find_local_client(_service, _instance); + if (its_offering_client == VSOMEIP_ROUTING_CLIENT) { + VSOMEIP_ERROR << __func__ << " didn't find offering client for service [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << "]"; + ret = false; + } else { + if (!stub_->send_provided_event_resend_request(its_offering_client, + pending_remote_offer_add(_service, _instance))) { + VSOMEIP_ERROR << __func__ << ": Couldn't send event resend" + << "request to client 0x" << std::hex << std::setw(4) + << std::setfill('0') << its_offering_client << " providing service [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << "]"; + + ret = false; + } + } + } + } + return ret; +} + +bool routing_manager_impl::stop_offer_service_remotely(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled) { + bool ret = true; + bool service_still_offered_remote(false); + // update service configuration + if (!configuration_->remote_offer_info_remove(_service, _instance, _port, + _reliable, _magic_cookies_enabled, &service_still_offered_remote)) { + VSOMEIP_ERROR << __func__ << " couldn't remove remote offer info for service [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << "] from configuration"; + ret = false; + } + std::shared_ptr<serviceinfo> its_info = find_service(_service, _instance); + std::shared_ptr<endpoint> its_server_endpoint; + if (its_info) { + its_server_endpoint = its_info->get_endpoint(_reliable); + } + // don't deregister events if the service is still offered remotely + if (!service_still_offered_remote) { + const client_t its_offering_client = find_local_client(_service, _instance); + major_version_t its_major(0); + minor_version_t its_minor(0); + if (its_info) { + its_major = its_info->get_major(); + its_minor = its_info->get_minor(); + } + // unset payload and clear subcribers + routing_manager_base::stop_offer_service(its_offering_client, + _service, _instance, its_major, its_minor); + // unregister events + for (const event_t its_event_id : find_events(_service, _instance)) { + unregister_shadow_event(its_offering_client, _service, _instance, + its_event_id, true); + } + clear_targets_and_pending_sub_from_eventgroups(_service, _instance); + clear_remote_subscriber(_service, _instance); + + if (discovery_ && its_info) { + discovery_->stop_offer_service(_service, _instance, its_info); + its_info->set_endpoint(std::shared_ptr<endpoint>(), _reliable); + } + } else { + // service is still partly offered + if (discovery_ && its_info) { + std::shared_ptr<serviceinfo> its_copied_info = + std::make_shared<serviceinfo>(*its_info); + its_info->set_endpoint(std::shared_ptr<endpoint>(), _reliable); + // ensure to not send StopOffer for endpoint on which the service is + // still offered + its_copied_info->set_endpoint(std::shared_ptr<endpoint>(), !_reliable); + discovery_->stop_offer_service(_service, _instance, its_copied_info); + } + } + + cleanup_server_endpoint(_service, its_server_endpoint); + return ret; +} + void routing_manager_impl::on_message(const byte_t *_data, length_t _size, endpoint *_receiver, const boost::asio::ip::address &_destination, client_t _bound_client, @@ -1119,6 +1261,9 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, client_t requester = VSOMEIP_BYTES_TO_WORD( _data[VSOMEIP_CLIENT_POS_MIN], _data[VSOMEIP_CLIENT_POS_MAX]); + its_method = VSOMEIP_BYTES_TO_WORD( + _data[VSOMEIP_METHOD_POS_MIN], + _data[VSOMEIP_METHOD_POS_MAX]); if (!configuration_->is_offered_remote(its_service, its_instance)) { VSOMEIP_WARNING << std::hex << "Security: Received a remote request " << "for service/instance " << its_service << "/" << its_instance @@ -1131,11 +1276,13 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, << " which is already used locally ~> Skip message!"; return; } - if (!configuration_->is_client_allowed(requester, its_service, its_instance)) { - VSOMEIP_WARNING << std::hex << "Security: Received a remote request " - << "from client 0x" << requester << " for service/instance " + if (!configuration_->is_remote_client_allowed()) { + // check if policy allows remote requests. + VSOMEIP_WARNING << "routing_manager_impl::on_message: " + << std::hex << "Security: Remote client with client ID 0x" << requester + << " is not allowed to communicate with service/instance/method " << its_service << "/" << its_instance - << " which violates the security policy ~> Skip message!"; + << "/" << its_method; return; } } @@ -1167,7 +1314,8 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, #ifdef USE_DLT is_forwarded = #endif - on_message(its_service, its_instance, _data, _size, _receiver->is_reliable(), its_is_crc_valid); + on_message(its_service, its_instance, _data, _size, _receiver->is_reliable(), + _bound_client, its_is_crc_valid, true); } } @@ -1177,14 +1325,14 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; const boost::asio::ip::address_v4 its_remote_address = _remote_address.is_v4() ? _remote_address.to_v4() : boost::asio::ip::address_v4::from_string("6.6.6.6"); - tc::protocol_e its_protocol = - _receiver->is_local() ? tc::protocol_e::local : - _receiver->is_reliable() ? tc::protocol_e::tcp : - tc::protocol_e::udp; + trace::protocol_e its_protocol = + _receiver->is_local() ? trace::protocol_e::local : + _receiver->is_reliable() ? trace::protocol_e::tcp : + trace::protocol_e::udp; its_header.prepare(its_remote_address, _remote_port, its_protocol, false, its_instance); tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, @@ -1196,7 +1344,9 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, bool routing_manager_impl::on_message( service_t _service, instance_t _instance, const byte_t *_data, length_t _size, - bool _reliable, bool _is_valid_crc) { + bool _reliable, client_t _bound_client, + bool _is_valid_crc, + bool _is_from_remote) { #if 0 std::stringstream msg; msg << "rmi::on_message(" @@ -1219,11 +1369,13 @@ bool routing_manager_impl::on_message( if (its_client == VSOMEIP_ROUTING_CLIENT && utility::is_notification(_data[VSOMEIP_MESSAGE_TYPE_POS])) { - is_forwarded = deliver_notification(_service, _instance, _data, _size, _reliable, _is_valid_crc); + is_forwarded = deliver_notification(_service, _instance, _data, _size, + _reliable, _bound_client, _is_valid_crc, _is_from_remote); } else if (its_client == host_->get_client()) { - deliver_message(_data, _size, _instance, _reliable, _is_valid_crc); + deliver_message(_data, _size, _instance, + _reliable, _bound_client, _is_valid_crc, _is_from_remote); } else { - send(its_client, _data, _size, _instance, true, _reliable, _is_valid_crc); //send to proxy + send(its_client, _data, _size, _instance, true, _reliable, _bound_client, _is_valid_crc, _is_from_remote); //send to proxy } return is_forwarded; } @@ -1244,7 +1396,7 @@ void routing_manager_impl::on_notification(client_t _client, its_length); if (_notify_one) { - notify_one(_service, _instance, its_event->get_event(), its_payload, _client, true, true); + notify_one(_service, _instance, its_event->get_event(), its_payload, _client, true, true, false); } else { if (its_event->is_field()) { if (its_event->is_set()) { @@ -1311,6 +1463,13 @@ void routing_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { bool reliable_; std::shared_ptr<endpoint> endpoint_; }; + + // Set to state CONNECTED as connection is not yet fully established in remote side POV + // but endpoint is ready to send / receive. Set to ESTABLISHED after timer expires + // to prevent inserting subscriptions twice or send out subscription before remote side + // is finished with TCP 3 way handshake + _endpoint->set_connected(true); + std::forward_list<struct service_info> services_to_report_; { std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); @@ -1324,7 +1483,7 @@ void routing_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { if (found_endpoint->second.get() == _endpoint.get()) { std::shared_ptr<serviceinfo> its_info(find_service(its_service.first, its_instance.first)); if (!its_info) { - _endpoint->set_connected(true); + _endpoint->set_established(true); return; } services_to_report_.push_front( @@ -1340,7 +1499,7 @@ void routing_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { if (found_endpoint->second.get() == _endpoint.get()) { std::shared_ptr<serviceinfo> its_info(find_service(its_service.first, its_instance.first)); if (!its_info) { - _endpoint->set_connected(true); + _endpoint->set_established(true); return; } services_to_report_.push_front( @@ -1356,6 +1515,7 @@ void routing_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { } } } + for (const auto &s : services_to_report_) { on_availability(s.service_id_, s.instance_id_, true, s.major_, s.minor_); if (s.reliable_) { @@ -1379,7 +1539,7 @@ void routing_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { } } if (services_to_report_.empty()) { - _endpoint->set_connected(true); + _endpoint->set_established(true); } } @@ -1488,7 +1648,6 @@ void routing_manager_impl::on_stop_offer_service(client_t _client, service_t _se if (discovery_) { if (its_info) { if (its_info->get_major() == _major && its_info->get_minor() == _minor) { - its_info->set_ttl(0); discovery_->stop_offer_service(_service, _instance, its_info); } } @@ -1498,59 +1657,21 @@ void routing_manager_impl::on_stop_offer_service(client_t _client, service_t _se // Cleanup reliable & unreliable server endpoints hold before if (its_info) { - std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); - std::shared_ptr<endpoint> its_empty_endpoint; - bool reliable = true; - - // Loop over reliable/unreliable and cleanup if needed - for (uint8_t i = 0; i < 2; ++i) { - std::shared_ptr<endpoint> its_endpoint; - if (reliable) { - its_endpoint = its_reliable_endpoint; - } else { - its_endpoint = its_unreliable_endpoint; - } - if (!its_endpoint) { - reliable = !reliable; - continue; - } - - // Check whether any service still uses this endpoint - its_endpoint->decrement_use_count(); - bool isLastService = (its_endpoint->get_use_count() == 0); - - // Clear service_instances_ - if (1 >= service_instances_[_service].size()) { - service_instances_.erase(_service); - } else { - service_instances_[_service].erase(its_endpoint.get()); - } - - // Clear server endpoint if no service remains using it - if (isLastService) { - uint16_t port = its_endpoint->get_local_port(); - if (server_endpoints_.find(port) != server_endpoints_.end()) { - server_endpoints_[port].erase(reliable); - if (server_endpoints_[port].find(!reliable) == server_endpoints_[port].end()) { - server_endpoints_.erase(port); - } - } - - // Stop endpoint (close socket) to release its async_handlers! - its_endpoint->stop(); - } - + if (its_unreliable_endpoint) { + cleanup_server_endpoint(_service, its_unreliable_endpoint); // Clear service info and service group - clear_service_info(_service, _instance, reliable); - - // Invert reliable flag and loop again - reliable = !reliable; + clear_service_info(_service, _instance, false); + } + if (its_reliable_endpoint) { + cleanup_server_endpoint(_service, its_reliable_endpoint); + // Clear service info and service group + clear_service_info(_service, _instance, true); } } } bool routing_manager_impl::deliver_message(const byte_t *_data, length_t _size, - instance_t _instance, bool _reliable, bool _is_valid_crc) { + instance_t _instance, bool _reliable, client_t _bound_client, bool _is_valid_crc, bool _is_from_remote) { bool is_delivered(false); auto a_deserializer = get_deserializer(); @@ -1563,6 +1684,109 @@ bool routing_manager_impl::deliver_message(const byte_t *_data, length_t _size, its_message->set_instance(_instance); its_message->set_reliable(_reliable); its_message->set_is_valid_crc(_is_valid_crc); + + if (!_is_from_remote) { + if (utility::is_notification(its_message->get_message_type())) { + if (!is_response_allowed(_bound_client, its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message: " + << std::hex << " received a notification from client 0x" << _bound_client + << " which does not offer service/instance/event " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " ~> Skip message!"; + return false; + } else { + if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message: " + << " isn't allowed to receive a notification from service/instance/event " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " respectively from client 0x" << _bound_client + << " ~> Skip message!"; + return false; + } + } + } else if (utility::is_request(its_message->get_message_type())) { + if (configuration_->is_security_enabled() + && its_message->get_client() != _bound_client) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message:" + << " received a request from client 0x" << std::setw(4) << std::setfill('0') + << its_message->get_client() << " to service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() << " which doesn't match the bound client 0x" + << std::setw(4) << std::setfill('0') << _bound_client + << " ~> skip message!"; + return false; + } + + if (!configuration_->is_client_allowed(its_message->get_client(), + its_message->get_service(), its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message: " + << " isn't allowed to send a request to service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " ~> Skip message!"; + return false; + } + } else { // response + if (!is_response_allowed(_bound_client, its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message: " + << " received a response from client 0x" << _bound_client + << " which does not offer service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " ~> Skip message!"; + return false; + } else { + if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message: " + << " isn't allowed to receive a response from service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " respectively from client 0x" << _bound_client + << " ~> Skip message!"; + return false; + } + } + } + } else { + if (!configuration_->is_remote_client_allowed()) { + // if the message is from remote, check if + // policy allows remote requests. + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message: " + << std::hex << "Remote clients are not allowed" + << " to communicate with service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " respectively with client 0x" << get_client() + << " ~> Skip message!"; + return false; + } else if (utility::is_notification(its_message->get_message_type())) { + if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_impl::deliver_message: " + << " isn't allowed to receive a notification from service/instance/event " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " respectively from remote client" + << " ~> Skip message!"; + return false; + } + } + } + host_->on_message(std::move(its_message)); is_delivered = true; } else { @@ -1575,7 +1799,8 @@ bool routing_manager_impl::deliver_message(const byte_t *_data, length_t _size, bool routing_manager_impl::deliver_notification( service_t _service, instance_t _instance, const byte_t *_data, length_t _length, - bool _reliable, bool _is_valid_crc) { + bool _reliable, client_t _bound_client, + bool _is_valid_crc, bool _is_from_remote) { method_t its_method = VSOMEIP_BYTES_TO_WORD(_data[VSOMEIP_METHOD_POS_MIN], _data[VSOMEIP_METHOD_POS_MAX]); @@ -1605,6 +1830,10 @@ bool routing_manager_impl::deliver_notification( } } const uint32_t its_length(utility::get_payload_size(_data, _length)); + if (its_length != _length - VSOMEIP_FULL_HEADER_SIZE) { + VSOMEIP_ERROR << "Message length mismatch, dropping message!"; + return false; + } std::shared_ptr<payload> its_payload = runtime::get()->create_payload(&_data[VSOMEIP_PAYLOAD_POS], its_length); @@ -1616,7 +1845,7 @@ bool routing_manager_impl::deliver_notification( for (const auto its_local_client : its_event->get_subscribers()) { if (its_local_client == host_->get_client()) { - deliver_message(_data, _length, _instance, _reliable, _is_valid_crc); + deliver_message(_data, _length, _instance, _reliable, _bound_client, _is_valid_crc, _is_from_remote); } else { std::shared_ptr<endpoint> its_local_target = find_local(its_local_client); if (its_local_target) { @@ -1809,9 +2038,12 @@ std::shared_ptr<endpoint> routing_manager_impl::create_client_endpoint( configuration_->get_max_message_size_reliable( _address.to_string(), _remote_port), configuration_->get_buffer_shrink_threshold(), + // send timeout after 2/3 of configured ttl, warning after 1/3 std::chrono::milliseconds(configuration_->get_sd_ttl() * 666), configuration_->get_endpoint_queue_limit( - _address.to_string(), _remote_port)); + _address.to_string(), _remote_port), + configuration_->get_max_tcp_restart_aborts(), + configuration_->get_max_tcp_connect_time()); if (configuration_->has_enabled_magic_cookies(_address.to_string(), _remote_port)) { @@ -1827,7 +2059,8 @@ std::shared_ptr<endpoint> routing_manager_impl::create_client_endpoint( _local_port), boost::asio::ip::udp::endpoint(_address, _remote_port), io_, configuration_->get_endpoint_queue_limit( - _address.to_string(), _remote_port)); + _address.to_string(), _remote_port), + configuration_->get_udp_receive_buffer_size()); } } catch (...) { host_->on_error(error_code_e::CLIENT_ENDPOINT_CREATION_FAILED); @@ -1850,6 +2083,7 @@ std::shared_ptr<endpoint> routing_manager_impl::create_server_endpoint( configuration_->get_max_message_size_reliable( its_unicast.to_string(), _port), configuration_->get_buffer_shrink_threshold(), + // send timeout after 2/3 of configured ttl, warning after 1/3 std::chrono::milliseconds(configuration_->get_sd_ttl() * 666), configuration_->get_endpoint_queue_limit( its_unicast.to_string(), _port)); @@ -1872,7 +2106,8 @@ std::shared_ptr<endpoint> routing_manager_impl::create_server_endpoint( #endif boost::asio::ip::udp::endpoint ep(its_unicast, _port); its_endpoint = std::make_shared<udp_server_endpoint_impl>( - shared_from_this(), ep, io_, its_limit); + shared_from_this(), ep, io_, its_limit, + configuration_->get_udp_receive_buffer_size()); } } else { @@ -1917,7 +2152,7 @@ std::shared_ptr<endpoint> routing_manager_impl::find_or_create_server_endpoint( return (its_endpoint); } -void routing_manager_impl::remove_local(client_t _client) { +void routing_manager_impl::remove_local(client_t _client, bool _remove_uid) { auto clients_subscriptions = get_subscriptions(_client); { std::lock_guard<std::mutex> its_lock(remote_subscription_state_mutex_); @@ -1925,7 +2160,7 @@ void routing_manager_impl::remove_local(client_t _client) { remote_subscription_state_.erase(std::tuple_cat(s, std::make_tuple(_client))); } } - routing_manager_base::remove_local(_client, clients_subscriptions); + routing_manager_base::remove_local(_client, clients_subscriptions, _remove_uid); std::forward_list<std::pair<service_t, instance_t>> services_to_release_; { @@ -2300,7 +2535,7 @@ void routing_manager_impl::add_routing_info( its_info->get_minor()); if (discovery_) { std::shared_ptr<endpoint> ep = its_info->get_endpoint(true); - if (ep && ep->is_connected()) { + if (ep && ep->is_established()) { discovery_->on_endpoint_connected( _service, _instance, ep); @@ -2361,7 +2596,7 @@ void routing_manager_impl::add_routing_info( } if (discovery_) { std::shared_ptr<endpoint> ep = its_info->get_endpoint(false); - if (ep && ep->is_connected()) { + if (ep && ep->is_established()) { discovery_->on_endpoint_connected(_service, _instance, ep); } } @@ -2391,7 +2626,7 @@ void routing_manager_impl::add_routing_info( its_info->get_minor()); if (discovery_) { std::shared_ptr<endpoint> ep = its_info->get_endpoint(false); - if (ep && ep->is_connected()) { + if (ep && ep->is_established()) { discovery_->on_endpoint_connected( _service, _instance, ep); @@ -2417,33 +2652,12 @@ void routing_manager_impl::del_routing_info(service_t _service, instance_t _inst on_availability(_service, _instance, false, its_info->get_major(), its_info->get_minor()); stub_->on_stop_offer_service(VSOMEIP_ROUTING_CLIENT, _service, _instance, its_info->get_major(), its_info->get_minor()); // Implicit unsubscribe - { - std::lock_guard<std::mutex> its_lock(eventgroups_mutex_); - auto found_service = eventgroups_.find(_service); - if (found_service != eventgroups_.end()) { - auto found_instance = found_service->second.find(_instance); - if (found_instance != found_service->second.end()) { - for (auto &its_eventgroup : found_instance->second) { - its_eventgroup.second->clear_targets(); - its_eventgroup.second->clear_pending_subscriptions(); - } - } - } - } + clear_targets_and_pending_sub_from_eventgroups(_service, _instance); clear_identified_clients( _service, _instance); clear_identifying_clients( _service, _instance); - { - std::lock_guard<std::mutex> its_lock(remote_subscribers_mutex_); - auto found_service = remote_subscribers_.find(_service); - if (found_service != remote_subscribers_.end()) { - if (found_service->second.erase(_instance) > 0 && - !found_service->second.size()) { - remote_subscribers_.erase(found_service); - } - } - } + clear_remote_subscriber(_service, _instance); if (_has_reliable) { clear_client_endpoints(_service, _instance, true); @@ -2509,11 +2723,8 @@ void routing_manager_impl::update_routing_info(std::chrono::milliseconds _elapse void routing_manager_impl::expire_services(const boost::asio::ip::address &_address) { std::map<service_t, std::vector<instance_t> > its_expired_offers; - for (auto &s : get_services()) { + for (auto &s : get_services_remote()) { for (auto &i : s.second) { - if (find_local_client(s.first, i.first) != VSOMEIP_ROUTING_CLIENT) { - continue; //don't expire local services - } bool is_gone(false); boost::asio::ip::address its_address; std::shared_ptr<client_endpoint> its_client_endpoint = @@ -2919,7 +3130,14 @@ void routing_manager_impl::on_subscribe_ack(client_t _client, if (specific_endpoint_client) { if (_client == get_client()) { host_->on_subscription_error(_service, _instance, _eventgroup, 0x0 /*OK*/); - host_->on_subscription_status(_service, _instance, _eventgroup, _event, 0x0 /*OK*/); + if (_event == ANY_EVENT) { + for (const auto &its_event : its_eventgroup->get_events()) + host_->on_subscription_status(_service, _instance, + _eventgroup, its_event->get_event(), 0x0 /*OK*/); + } else { + host_->on_subscription_status(_service, _instance, + _eventgroup, _event, 0x0 /*OK*/); + } } else { stub_->send_subscribe_ack(_client, _service, _instance, _eventgroup, _event); @@ -3068,7 +3286,7 @@ bool routing_manager_impl::deliver_specific_endpoint_message(service_t _service, _receiver->is_reliable(), VSOMEIP_SEND); } } else { - deliver_message(_data, _size, _instance, _receiver->is_reliable()); + deliver_message(_data, _size, _instance, _receiver->is_reliable(), VSOMEIP_ROUTING_CLIENT, true, true); } return true; } @@ -3484,7 +3702,7 @@ void routing_manager_impl::clear_remote_subscriber( } std::chrono::steady_clock::time_point -routing_manager_impl::expire_subscriptions() { +routing_manager_impl::expire_subscriptions(bool _force) { struct subscriptions_info { service_t service_id_; instance_t instance_id_; @@ -3507,10 +3725,14 @@ routing_manager_impl::expire_subscriptions() { for (auto &its_eventgroup : its_instance.second) { std::set<std::shared_ptr<endpoint_definition>> its_expired_endpoints; for (auto &its_target : its_eventgroup.second->get_targets()) { - if (its_target.expiration_ < now) { + if (_force) { its_expired_endpoints.insert(its_target.endpoint_); - } else if (its_target.expiration_ < next_expiration) { - next_expiration = its_target.expiration_; + } else { + if (its_target.expiration_ < now) { + its_expired_endpoints.insert(its_target.endpoint_); + } else if (its_target.expiration_ < next_expiration) { + next_expiration = its_target.expiration_; + } } } @@ -3576,7 +3798,8 @@ routing_manager_impl::expire_subscriptions() { << std::hex << std::setfill('0') << std::setw(4) << s.eventgroup_id_ << "] from " << s.invalid_endpoint_->get_address() << ":" << std::dec << s.invalid_endpoint_->get_port() - << "(" << std::hex << std::setfill('0') << std::setw(4) << s.client_ << ")"; + << "(" << std::hex << std::setfill('0') << std::setw(4) << s.client_ << ") " + << _force; } } return next_expiration; @@ -3593,8 +3816,18 @@ void routing_manager_impl::log_version_timer_cbk(boost::system::error_code const if (discovery_) { is_diag_mode = discovery_->get_diagnosis_mode(); } + std::stringstream its_last_resume; + { + std::lock_guard<std::mutex> its_lock(last_resume_mutex_); + if (last_resume_ != std::chrono::steady_clock::time_point::min()) { + its_last_resume << " | " << std::dec + << std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::steady_clock::now() - last_resume_).count() << "s"; + } + } VSOMEIP_INFO << "vSomeIP " << VSOMEIP_VERSION << " | (" - << ((is_diag_mode == true) ? "diagnosis)" : "default)"); + << ((is_diag_mode == true) ? "diagnosis)" : "default)") + << its_last_resume.str(); { std::lock_guard<std::mutex> its_lock(version_log_timer_mutex_); version_log_timer_.expires_from_now( @@ -3838,6 +4071,8 @@ void routing_manager_impl::register_client_error_handler(client_t _client, } void routing_manager_impl::handle_client_error(client_t _client) { + VSOMEIP_INFO << "Client 0x" << std::hex << get_client() + << " handles a client error(" << std::hex << _client << ")"; if (stub_) stub_->update_registration(_client, registration_type_e::DEREGISTER_ON_ERROR); @@ -4023,14 +4258,26 @@ void routing_manager_impl::set_routing_state(routing_state_e _routing_state) { // stop processing of incoming SD messages discovery_->stop(); + // remove all remote subscriptions to remotely offered services on this node + expire_subscriptions(true); + // send StopOffer messages for remotely offered services on this node for (const auto &its_service : get_offered_services()) { for (const auto &its_instance : its_service.second) { - its_instance.second->set_ttl(0); + if (its_instance.second->get_endpoint(true) || its_instance.second->get_endpoint(false)) { + const client_t its_client(find_local_client(its_service.first, its_instance.first)); + VSOMEIP_WARNING << "service " + << std::hex << std::setw(4) << std::setfill('0') << its_service.first << "." + << std::hex << std::setw(4) << std::setfill('0') << its_instance.first << " still offered by " + << std::hex << std::setw(4) << std::setfill('0') << its_client; + } discovery_->stop_offer_service(its_service.first, its_instance.first, its_instance.second); } } - + { + std::lock_guard<std::mutex> its_lock(remote_subscription_state_mutex_); + remote_subscription_state_.clear(); + } // mark all external services as offline services_t its_remote_services; { @@ -4065,6 +4312,10 @@ void routing_manager_impl::set_routing_state(routing_state_e _routing_state) { { VSOMEIP_INFO << "Set routing to resume mode, diagnosis mode was " << ((discovery_->get_diagnosis_mode() == true) ? "active." : "inactive."); + { + std::lock_guard<std::mutex> its_lock(last_resume_mutex_); + last_resume_ = std::chrono::steady_clock::now(); + } // Reset relevant in service info for (const auto &its_service : get_offered_services()) { @@ -4076,6 +4327,10 @@ void routing_manager_impl::set_routing_state(routing_state_e _routing_state) { // Switch SD back to normal operation discovery_->set_diagnosis_mode(false); + if (routing_state_handler_) { + routing_state_handler_(_routing_state); + } + // start processing of SD messages (incoming remote offers should lead to new subscribe messages) discovery_->start(); @@ -4098,7 +4353,6 @@ void routing_manager_impl::set_routing_state(routing_state_e _routing_state) { for (const auto &its_instance : its_service.second) { if (host_->get_configuration()->is_someip( its_service.first, its_instance.first)) { - its_instance.second->set_ttl(0); discovery_->stop_offer_service( its_service.first, its_instance.first, its_instance.second); } @@ -4192,6 +4446,9 @@ void routing_manager_impl::on_net_interface_or_route_state_changed( } void routing_manager_impl::start_ip_routing() { + if (routing_ready_handler_) { + routing_ready_handler_(); + } if (discovery_) { discovery_->start(); } else { @@ -4262,6 +4519,62 @@ routing_manager_impl::get_subscribed_eventgroups( return its_eventgroups; } +void routing_manager_impl::clear_targets_and_pending_sub_from_eventgroups( + service_t _service, instance_t _instance) { + std::vector<std::shared_ptr<event>> its_events; + { + std::lock_guard<std::mutex> its_lock(eventgroups_mutex_); + auto found_service = eventgroups_.find(_service); + if (found_service != eventgroups_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + for (const auto &its_eventgroup : found_instance->second) { + // As the service is gone, all subscriptions to its events + // do no longer exist and the last received payload is no + // longer valid. + for (auto &its_event : its_eventgroup.second->get_events()) { + const auto its_subscribers = its_event->get_subscribers(); + for (const auto its_subscriber : its_subscribers) { + if (its_subscriber != get_client()) { + its_event->remove_subscriber( + its_eventgroup.first, its_subscriber); + } + + client_t its_client = is_specific_endpoint_client(its_subscriber, _service, _instance); + { + std::lock_guard<std::mutex> its_lock(remote_subscription_state_mutex_); + const auto its_tuple = + std::make_tuple(found_service->first, found_instance->first, + its_eventgroup.first, its_client); + remote_subscription_state_.erase(its_tuple); + } + } + its_events.push_back(its_event); + } + its_eventgroup.second->clear_targets(); + its_eventgroup.second->clear_pending_subscriptions(); + } + } + } + } + for (const auto& e : its_events) { + e->unset_payload(true); + } +} + +void routing_manager_impl::clear_remote_subscriber(service_t _service, + instance_t _instance) { + std::lock_guard<std::mutex> its_lock(remote_subscribers_mutex_); + auto found_service = remote_subscribers_.find(_service); + if (found_service != remote_subscribers_.end()) { + if (found_service->second.erase(_instance) > 0 && + !found_service->second.size()) { + remote_subscribers_.erase(found_service); + } + } +} + + void routing_manager_impl::call_sd_endpoint_connected( const boost::system::error_code& _error, service_t _service, instance_t _instance, @@ -4271,7 +4584,7 @@ void routing_manager_impl::call_sd_endpoint_connected( if (_error) { return; } - _endpoint->set_connected(true); + _endpoint->set_established(true); if (discovery_) { discovery_->on_endpoint_connected(_service, _instance, _endpoint); @@ -4423,6 +4736,39 @@ void routing_manager_impl::send_initial_events( } } +void routing_manager_impl::register_offer_acceptance_handler( + vsomeip::offer_acceptance_handler_t _handler) const { + if (discovery_) { + discovery_->register_offer_acceptance_handler(_handler); + } +} + +void routing_manager_impl::register_reboot_notification_handler( + vsomeip::reboot_notification_handler_t _handler) const { + if (discovery_) { + discovery_->register_reboot_notification_handler(_handler); + } +} + +void routing_manager_impl::register_routing_ready_handler( + routing_ready_handler_t _handler) { + routing_ready_handler_ = _handler; +} + +void routing_manager_impl::register_routing_state_handler( + routing_state_handler_t _handler) { + routing_state_handler_ = _handler; +} + +void routing_manager_impl::offer_acceptance_enabled( + boost::asio::ip::address _address) { + boost::system::error_code ec; + VSOMEIP_INFO << "ipsec-plugin-mgu: expire subscriptions and services: " + << _address.to_string(ec); + expire_subscriptions(_address); + expire_services(_address); +} + void routing_manager_impl::memory_log_timer_cbk( boost::system::error_code const & _error) { if (_error) { @@ -4707,4 +5053,374 @@ void routing_manager_impl::send_subscription( } } +void routing_manager_impl::cleanup_server_endpoint( + service_t _service, const std::shared_ptr<endpoint>& _endpoint) { + if (_endpoint) { + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); + bool reliable = _endpoint->is_reliable(); + // Check whether any service still uses this endpoint + _endpoint->decrement_use_count(); + bool isLastService = (_endpoint->get_use_count() == 0); + + // Clear service_instances_ + if (1 >= service_instances_[_service].size()) { + service_instances_.erase(_service); + } else { + service_instances_[_service].erase(_endpoint.get()); + } + + // Clear server endpoint if no service remains using it + if (isLastService) { + const uint16_t port = _endpoint->get_local_port(); + if (server_endpoints_.find(port) != server_endpoints_.end()) { + server_endpoints_[port].erase(reliable); + if (server_endpoints_[port].find(!reliable) == server_endpoints_[port].end()) { + server_endpoints_.erase(port); + } + } + + // Stop endpoint (close socket) to release its async_handlers! + _endpoint->stop(); + } + } +} + +pending_remote_offer_id_t routing_manager_impl::pending_remote_offer_add( + service_t _service, instance_t _instance) { + std::lock_guard<std::mutex> its_lock(pending_remote_offers_mutex_); + if (++pending_remote_offer_id_ == 0) { + pending_remote_offer_id_++; + } + pending_remote_offers_[pending_remote_offer_id_] = std::make_pair(_service, + _instance); + return pending_remote_offer_id_; +} + +std::pair<service_t, instance_t> routing_manager_impl::pending_remote_offer_remove( + pending_remote_offer_id_t _id) { + std::lock_guard<std::mutex> its_lock(pending_remote_offers_mutex_); + std::pair<service_t, instance_t> ret = std::make_pair(ANY_SERVICE, + ANY_INSTANCE); + auto found_si = pending_remote_offers_.find(_id); + if (found_si != pending_remote_offers_.end()) { + ret = found_si->second; + pending_remote_offers_.erase(found_si); + } + return ret; +} + +void routing_manager_impl::on_resend_provided_events_response( + pending_remote_offer_id_t _id) { + const std::pair<service_t, instance_t> its_service = + pending_remote_offer_remove(_id); + if (its_service.first != ANY_SERVICE) { + // create server endpoint + std::shared_ptr<serviceinfo> its_info = find_service(its_service.first, + its_service.second); + if (its_info) { + its_info->set_ttl(DEFAULT_TTL); + init_service_info(its_service.first, its_service.second, true); + } + } +} + +void routing_manager_impl::on_security_update_timeout( + const boost::system::error_code& _error, + pending_security_update_id_t _id, + std::shared_ptr<boost::asio::steady_timer> _timer) { + (void)_timer; + if (_error) { + // timer was cancelled + return; + } + security_update_state_e its_state = security_update_state_e::SU_UNKNOWN_USER_ID; + std::unordered_set<client_t> its_missing_clients = pending_security_update_get(_id); + { + // erase timer + std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); + security_update_timers_.erase(_id); + } + { + // print missing responses and check if some clients did not respond because they already disconnected + if (!its_missing_clients.empty()) { + for (auto its_client : its_missing_clients) { + VSOMEIP_INFO << __func__ << ": Client 0x" << std::hex << its_client + << " did not respond to the policy update / removal with ID: 0x" << std::hex << _id; + if (!find_local(its_client)) { + VSOMEIP_INFO << __func__ << ": Client 0x" << std::hex << its_client + << " is not connected anymore, do not expect answer for policy update / removal with ID: 0x" + << std::hex << _id; + pending_security_update_remove(_id, its_client); + } + } + } + + its_missing_clients = pending_security_update_get(_id); + if (its_missing_clients.empty()) { + VSOMEIP_INFO << __func__ << ": Received all responses for " + "security update/removal ID: 0x" << std::hex << _id; + its_state = security_update_state_e::SU_SUCCESS; + } + { + // erase pending security update + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + pending_security_updates_.erase(_id); + } + + // call handler with error on timeout or with SUCCESS if missing clients are not connected + std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); + const auto found_handler = security_update_handlers_.find(_id); + if (found_handler != security_update_handlers_.end()) { + found_handler->second(its_state); + security_update_handlers_.erase(found_handler); + } else { + VSOMEIP_WARNING << __func__ << ": Callback not found for security update / removal with ID: 0x" + << std::hex << _id; + } + } +} + +bool routing_manager_impl::update_security_policy_configuration( + uint32_t _uid, uint32_t _gid, + ::std::shared_ptr<policy> _policy, std::shared_ptr<payload> _payload, security_update_handler_t _handler) { + bool ret(true); + // cache security policy payload for later distribution to new registering clients + stub_->policy_cache_add(_uid, _payload); + + // update security policy from configuration + configuration_->update_security_policy(_uid, _gid, _policy); + + // determine currently connected clients + std::unordered_set<client_t> its_clients_to_inform = get_connected_clients(); + + // add handler + pending_security_update_id_t its_id; + if (!its_clients_to_inform.empty()) { + its_id = pending_security_update_add(its_clients_to_inform); + { + std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); + security_update_handlers_[its_id] = _handler; + } + + { + std::shared_ptr<boost::asio::steady_timer> its_timer = + std::make_shared<boost::asio::steady_timer>(io_); + boost::system::error_code ec; + its_timer->expires_from_now(std::chrono::milliseconds(3000), ec); + if (!ec) { + its_timer->async_wait( + std::bind( + &routing_manager_impl::on_security_update_timeout, + std::static_pointer_cast<routing_manager_impl>( + shared_from_this()), + std::placeholders::_1, its_id, its_timer)); + } else { + VSOMEIP_ERROR << __func__ << ": timer creation: " << ec.message(); + } + std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); + security_update_timers_[its_id] = its_timer; + } + + // trigger all currently connected clients to update the security policy + uint32_t sent_counter(0); + uint32_t its_tranche = + uint32_t(its_clients_to_inform.size() >= 10 ? (its_clients_to_inform.size() / 10) : 1); + VSOMEIP_INFO << __func__ << ": Informing [" << std::dec << its_clients_to_inform.size() + << "] currently connected clients about policy update for UID: " + << std::dec << _uid << " with update ID: 0x" << std::hex << its_id; + for (auto its_client : its_clients_to_inform) { + if (!stub_->send_update_security_policy_request(its_client, its_id, _uid, _payload)) { + VSOMEIP_INFO << __func__ << ": Couldn't send update security policy " + << "request to client 0x" << std::hex << std::setw(4) + << std::setfill('0') << its_client << " policy UID: " + << std::hex << std::setw(4) << std::setfill('0') << _uid << " GID: " + << std::hex << std::setw(4) << std::setfill('0') << _gid + << " with update ID: 0x" << std::hex << its_id + << " as client already disconnected"; + // remove client from expected answer list + pending_security_update_remove(its_id, its_client); + } + sent_counter++; + // Prevent burst + if (sent_counter % its_tranche == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + } else { + // if routing manager has no client call the handler directly + _handler(security_update_state_e::SU_SUCCESS); + } + return ret; +} + +bool routing_manager_impl::remove_security_policy_configuration( + uint32_t _uid, uint32_t _gid, security_update_handler_t _handler) { + bool ret(true); + + // remove security policy from configuration (only if there was a updateACL call before) + if (stub_->is_policy_cached(_uid)) { + if (!configuration_->remove_security_policy(_uid, _gid)) { + _handler(vsomeip::security_update_state_e::SU_UNKNOWN_USER_ID); + ret = false; + } else { + // remove policy from cache to prevent sending it to registering clients + stub_->policy_cache_remove(_uid); + + // add handler + pending_security_update_id_t its_id; + + // determine currently connected clients + std::unordered_set<client_t> its_clients_to_inform = get_connected_clients(); + + if (!its_clients_to_inform.empty()) { + its_id = pending_security_update_add(its_clients_to_inform); + { + std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); + security_update_handlers_[its_id] = _handler; + } + + { + std::shared_ptr<boost::asio::steady_timer> its_timer = + std::make_shared<boost::asio::steady_timer>(io_); + boost::system::error_code ec; + its_timer->expires_from_now(std::chrono::milliseconds(3000), ec); + if (!ec) { + its_timer->async_wait( + std::bind( + &routing_manager_impl::on_security_update_timeout, + std::static_pointer_cast<routing_manager_impl>( + shared_from_this()), + std::placeholders::_1, its_id, its_timer)); + } else { + VSOMEIP_ERROR << __func__ << ": timer creation: " << ec.message(); + } + std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); + security_update_timers_[its_id] = its_timer; + } + + // trigger all clients to remove the security policy + uint32_t sent_counter(0); + uint32_t its_tranche = + uint32_t(its_clients_to_inform.size() >= 10 ? (its_clients_to_inform.size() / 10) : 1); + VSOMEIP_INFO << __func__ << ": Informing [" << std::dec << its_clients_to_inform.size() + << "] currently connected clients about policy removal for UID: " + << std::dec << _uid << " with update ID: " << its_id; + for (auto its_client : its_clients_to_inform) { + if (!stub_->send_remove_security_policy_request(its_client, its_id, _uid, _gid)) { + VSOMEIP_INFO << __func__ << ": Couldn't send remove security policy " + << "request to client 0x" << std::hex << std::setw(4) + << std::setfill('0') << its_client << " policy UID: " + << std::hex << std::setw(4) << std::setfill('0') << _uid << " GID: " + << std::hex << std::setw(4) << std::setfill('0') << _gid + << " with update ID: 0x" << std::hex << its_id + << " as client already disconnected"; + // remove client from expected answer list + pending_security_update_remove(its_id, its_client); + } + sent_counter++; + // Prevent burst + if (sent_counter % its_tranche == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + } else { + // if routing manager has no client call the handler directly + _handler(security_update_state_e::SU_SUCCESS); + } + } + } + else { + _handler(vsomeip::security_update_state_e::SU_UNKNOWN_USER_ID); + ret = false; + } + return ret; +} + +pending_security_update_id_t routing_manager_impl::pending_security_update_add( + std::unordered_set<client_t> _clients) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + if (++pending_security_update_id_ == 0) { + pending_security_update_id_++; + } + pending_security_updates_[pending_security_update_id_] = _clients; + return pending_security_update_id_; +} + +std::unordered_set<client_t> routing_manager_impl::pending_security_update_get( + pending_security_update_id_t _id) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + std::unordered_set<client_t> its_missing_clients; + auto found_si = pending_security_updates_.find(_id); + if (found_si != pending_security_updates_.end()) { + its_missing_clients = pending_security_updates_[_id]; + } + return its_missing_clients; +} + +bool routing_manager_impl::pending_security_update_remove( + pending_security_update_id_t _id, client_t _client) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + auto found_si = pending_security_updates_.find(_id); + if (found_si != pending_security_updates_.end()) { + if (found_si->second.erase(_client)) { + return true; + } + } + return false; +} + +bool routing_manager_impl::is_pending_security_update_finished( + pending_security_update_id_t _id) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + bool ret(false); + auto found_si = pending_security_updates_.find(_id); + if (found_si != pending_security_updates_.end()) { + if (!found_si->second.size()) { + ret = true; + } + } + if (ret) { + pending_security_updates_.erase(_id); + } + return ret; +} + +void routing_manager_impl::on_security_update_response( + pending_security_update_id_t _id, client_t _client) { + if (pending_security_update_remove(_id, _client)) { + if (is_pending_security_update_finished(_id)) { + // cancel timeout timer + { + std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); + auto found_timer = security_update_timers_.find(_id); + if (found_timer != security_update_timers_.end()) { + boost::system::error_code ec; + found_timer->second->cancel(ec); + security_update_timers_.erase(found_timer); + } else { + VSOMEIP_WARNING << __func__ << ": Received all responses " + "for security update/removal ID: 0x" + << std::hex << _id << " but timeout already happened"; + } + } + + // call handler + { + std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); + auto found_handler = security_update_handlers_.find(_id); + if (found_handler != security_update_handlers_.end()) { + found_handler->second(security_update_state_e::SU_SUCCESS); + security_update_handlers_.erase(found_handler); + VSOMEIP_INFO << __func__ << ": Received all responses for " + "security update/removal ID: 0x" << std::hex << _id; + } else { + VSOMEIP_WARNING << __func__ << ": Received all responses " + "for security update/removal ID: 0x" + << std::hex << _id << " but didn't find handler"; + } + } + } + } +} + } // namespace vsomeip diff --git a/implementation/routing/src/routing_manager_proxy.cpp b/implementation/routing/src/routing_manager_proxy.cpp index 8d48b25..d439654 100644 --- a/implementation/routing/src/routing_manager_proxy.cpp +++ b/implementation/routing/src/routing_manager_proxy.cpp @@ -10,12 +10,6 @@ #include <future> #include <forward_list> -#ifndef _WIN32 -// for umask -#include <sys/types.h> -#include <sys/stat.h> -#endif - #include <vsomeip/constants.hpp> #include <vsomeip/runtime.hpp> @@ -32,6 +26,10 @@ #include "../../service_discovery/include/runtime.hpp" #include "../../utility/include/byteorder.hpp" #include "../../utility/include/utility.hpp" +#include "../../configuration/include/policy.hpp" +#ifdef USE_DLT +#include "../../tracing/include/connector_impl.hpp" +#endif namespace vsomeip { @@ -94,8 +92,10 @@ void routing_manager_proxy::stop() { if (state_ == inner_state_type_e::ST_REGISTERING) { register_application_timer_.cancel(); } + + const std::chrono::milliseconds its_timeout(configuration_->get_shutdown_timeout()); while (state_ == inner_state_type_e::ST_REGISTERING) { - std::cv_status status = state_condition_.wait_for(its_lock, std::chrono::milliseconds(1000)); + std::cv_status status = state_condition_.wait_for(its_lock, its_timeout); if (status == std::cv_status::timeout) { VSOMEIP_WARNING << std::hex << client_ << " registering timeout on stop"; break; @@ -106,7 +106,7 @@ void routing_manager_proxy::stop() { deregister_application(); // Waiting de-register acknowledge to synchronize shutdown while (state_ == inner_state_type_e::ST_REGISTERED) { - std::cv_status status = state_condition_.wait_for(its_lock, std::chrono::milliseconds(1000)); + std::cv_status status = state_condition_.wait_for(its_lock, its_timeout); if (status == std::cv_status::timeout) { VSOMEIP_WARNING << std::hex << client_ << " couldn't deregister application - timeout"; break; @@ -137,7 +137,7 @@ void routing_manager_proxy::stop() { for (auto client: get_connected_clients()) { if (client != VSOMEIP_ROUTING_CLIENT) { - remove_local(client); + remove_local(client, true); } } @@ -437,6 +437,27 @@ void routing_manager_proxy::subscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, major_version_t _major, event_t _event, subscription_type_e _subscription_type) { { + if (_event == ANY_EVENT) { + if (!is_subscribe_to_any_event_allowed(_client, _service, _instance, _eventgroup)) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << _client + << " : routing_manager_proxy::subscribe: " + << " isn't allowed to subscribe to service/instance/event " + << _service << "/" << _instance << "/ANY_EVENT" + << " which violates the security policy ~> Skip subscribe!"; + return; + } + } else { + if (!configuration_->is_client_allowed(_client, _service, + _instance, _event)) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << _client + << " : routing_manager_proxy::subscribe: " + << " isn't allowed to subscribe to service/instance/event " + << _service << "/" << _instance + << "/" << _event; + return; + } + } + std::lock_guard<std::mutex> its_lock(state_mutex_); if (state_ == inner_state_type_e::ST_REGISTERED && is_available(_service, _instance, _major)) { send_subscribe(_client, _service, _instance, _eventgroup, _major, @@ -617,8 +638,12 @@ bool routing_manager_proxy::send(client_t _client, const byte_t *_data, length_t _size, instance_t _instance, bool _flush, bool _reliable, - bool _is_valid_crc) { + client_t _bound_client, + bool _is_valid_crc, + bool _sent_from_remote) { (void)_client; + (void)_bound_client; + (void)_sent_from_remote; bool is_sent(false); bool has_remote_subscribers(false); { @@ -697,7 +722,7 @@ bool routing_manager_proxy::send(client_t _client, const byte_t *_data, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(nullptr, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -741,7 +766,7 @@ bool routing_manager_proxy::send(client_t _client, const byte_t *_data, const uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(nullptr, true, _instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, _data, its_data_size); @@ -780,6 +805,7 @@ bool routing_manager_proxy::send_to( void routing_manager_proxy::on_connect(std::shared_ptr<endpoint> _endpoint) { _endpoint->set_connected(true); + _endpoint->set_established(true); { std::lock_guard<std::mutex> its_lock(sender_mutex_); if (_endpoint != sender_) { @@ -864,7 +890,16 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, std::memcpy(&its_length, &_data[VSOMEIP_COMMAND_SIZE_POS_MIN], sizeof(its_length)); - if (configuration_->is_security_enabled() && _bound_client != routing_host_id && + bool message_from_routing(false); + if (configuration_->is_security_enabled()) { + // if security is enabled, client ID of routing must be configured + // and credential passing is active. Otherwise bound client is zero by default + message_from_routing = (_bound_client == routing_host_id); + } else { + message_from_routing = (its_client == routing_host_id); + } + + if (configuration_->is_security_enabled() && !message_from_routing && _bound_client != its_client) { VSOMEIP_WARNING << std::hex << "Client " << std::setw(4) << std::setfill('0') << get_client() << " received a message with command " << (uint32_t)its_command @@ -877,20 +912,34 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, switch (its_command) { case VSOMEIP_SEND: { + if (_size < VSOMEIP_SEND_COMMAND_SIZE + VSOMEIP_FULL_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a SEND command with too small size -> skip!"; + break; + } instance_t its_instance; bool its_reliable; - bool its_is_vslid_crc; + bool its_is_valid_crc; std::memcpy(&its_instance,&_data[VSOMEIP_SEND_COMMAND_INSTANCE_POS_MIN], sizeof(instance_t)); std::memcpy(&its_reliable, &_data[VSOMEIP_SEND_COMMAND_RELIABLE_POS], sizeof(its_reliable)); - std::memcpy(&its_is_vslid_crc, &_data[VSOMEIP_SEND_COMMAND_VALID_CRC_POS], - sizeof(its_is_vslid_crc)); + std::memcpy(&its_is_valid_crc, &_data[VSOMEIP_SEND_COMMAND_VALID_CRC_POS], + sizeof(its_is_valid_crc)); // reduce by size of instance, flush, reliable, client and is_valid_crc flag const std::uint32_t its_message_size = its_length - (VSOMEIP_SEND_COMMAND_SIZE - VSOMEIP_COMMAND_HEADER_SIZE); + if (its_message_size != + VSOMEIP_BYTES_TO_LONG(_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 1], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 2], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 3]) + + VSOMEIP_SOMEIP_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a SEND command containing message with invalid size -> skip!"; + break; + } + auto a_deserializer = get_deserializer(); a_deserializer->set_data(&_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS], its_message_size); @@ -901,36 +950,110 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, if (its_message) { its_message->set_instance(its_instance); its_message->set_reliable(its_reliable); - its_message->set_is_valid_crc(its_is_vslid_crc); - if (utility::is_notification(its_message->get_message_type())) { - if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), - its_message->get_instance())) { - VSOMEIP_WARNING << std::hex << "Security: Client 0x" << get_client() - << " isn't allow receive a notification from to service/instance " - << its_message->get_service() << "/" << its_message->get_instance() - << " respectively from client 0x" << its_client - << " : Skip message!"; - return; - } - cache_event_payload(its_message); - } else if (utility::is_request(its_message->get_message_type())) { - if (!configuration_->is_client_allowed(its_message->get_client(), - its_message->get_service(), its_message->get_instance())) { - VSOMEIP_WARNING << std::hex << "Security: Client 0x" << its_message->get_client() - << " isn't allow to send a request to service/instance " - << its_message->get_service() << "/" << its_message->get_instance() - << " : Skip message!"; - return; + its_message->set_is_valid_crc(its_is_valid_crc); + if (!message_from_routing) { + if (utility::is_notification(its_message->get_message_type())) { + if (!is_response_allowed(_bound_client, its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << " received a notification from client 0x" << _bound_client + << " which does not offer service/instance/event " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " ~> Skip message!"; + return; + } else { + if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << " isn't allowed to receive a notification from service/instance/event " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " respectively from client 0x" << _bound_client + << " ~> Skip message!"; + return; + } + cache_event_payload(its_message); + } + } else if (utility::is_request(its_message->get_message_type())) { + if (configuration_->is_security_enabled() + && its_message->get_client() != _bound_client) { + VSOMEIP_WARNING << std::hex << "vSomeIP Security: Client 0x" << std::setw(4) << std::setfill('0') << get_client() + << " received a request from client 0x" << std::setw(4) << std::setfill('0') + << its_message->get_client() << " to service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() << " which doesn't match the bound client 0x" + << std::setw(4) << std::setfill('0') << _bound_client + << " ~> skip message!"; + return; + } + + if (!configuration_->is_client_allowed(its_message->get_client(), + its_message->get_service(), its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_message->get_client() + << " : routing_manager_proxy::on_message: " + << "isn't allowed to send a request to service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " ~> Skip message!"; + return; + } + } else { // response + if (!is_response_allowed(_bound_client, its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << " received a response from client 0x" << _bound_client + << " which does not offer service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " ~> Skip message!"; + return; + } else { + if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << " isn't allowed to receive a response from service/instance/method " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " respectively from client 0x" << _bound_client + << " ~> Skip message!"; + return; + } + } } - } else { // response - if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), - its_message->get_instance())) { - VSOMEIP_WARNING << std::hex << "Security: Client 0x" << get_client() - << " isn't allow receive a response from to service/instance " + } else { + if (!configuration_->is_remote_client_allowed()) { + // if the message is from routing manager, check if + // policy allows remote requests. + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << std::hex << "Security: Remote clients via routing manager with client ID 0x" << its_client + << " are not allowed to communicate with service/instance/method " << its_message->get_service() << "/" << its_message->get_instance() - << " respectively from client 0x" << its_client - << " : Skip message!"; + << "/" << its_message->get_method() + << " respectively with client 0x" << get_client() + << " ~> Skip message!"; return; + } else if (utility::is_notification(its_message->get_message_type())) { + // As subscription is sent on eventgroup level, incoming remote event ID's + // need to be checked as well if remote clients are allowed + // and the local policy only allows specific events in the eventgroup to be received. + if (!configuration_->is_client_allowed(get_client(), its_message->get_service(), + its_message->get_instance(), its_message->get_method())) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << " isn't allowed to receive a notification from service/instance/event " + << its_message->get_service() << "/" << its_message->get_instance() + << "/" << its_message->get_method() + << " respectively from remote clients via routing manager with client ID 0x" + << routing_host_id + << " ~> Skip message!"; + return; + } } } #ifdef USE_DLT @@ -938,10 +1061,10 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, && (client_side_logging_filter_.empty() || (1 == client_side_logging_filter_.count(std::make_tuple(its_message->get_service(), ANY_INSTANCE))) || (1 == client_side_logging_filter_.count(std::make_tuple(its_message->get_service(), its_message->get_instance()))))) { - tc::trace_header its_header; + trace::header its_header; if (its_header.prepare(nullptr, false, its_instance)) tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, - &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + &_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS], static_cast<std::uint16_t>(its_message_size)); } #endif @@ -950,20 +1073,29 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, VSOMEIP_ERROR << "Routing proxy: on_message: " << "SomeIP-Header deserialization failed!"; } - } break; + } case VSOMEIP_ROUTING_INFO: - if (!configuration_->is_security_enabled() ||_bound_client == routing_host_id) { + if (_size - VSOMEIP_COMMAND_HEADER_SIZE != its_length) { + VSOMEIP_WARNING << "Received a ROUTING_INFO command with invalid size -> skip!"; + break; + } + if (!configuration_->is_security_enabled() || message_from_routing) { on_routing_info(&_data[VSOMEIP_COMMAND_PAYLOAD_POS], its_length); } else { - VSOMEIP_WARNING << std::hex << "Security: Client 0x" << get_client() + VSOMEIP_WARNING << "routing_manager_proxy::on_message: " + << std::hex << "Security: Client 0x" << get_client() << " received an routing info from a client which isn't the routing manager" << " : Skip message!"; } break; case VSOMEIP_PING: + if (_size != VSOMEIP_PING_COMMAND_SIZE) { + VSOMEIP_WARNING << "Received a PING command with wrong size ~> skip!"; + break; + } send_pong(); VSOMEIP_TRACE << "PING(" << std::hex << std::setw(4) << std::setfill('0') << client_ << ")"; @@ -990,6 +1122,8 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, std::unique_lock<std::recursive_mutex> its_lock(incoming_subscriptions_mutex_); if (its_subscription_id != DEFAULT_SUBSCRIPTION) { its_lock.unlock(); + routing_manager_base::set_incoming_subscription_state(its_client, its_service, + its_instance, its_eventgroup, its_event, subscription_state_e::IS_SUBSCRIBING); // Remote subscriber: Notify routing manager initially + count subscribes auto self = shared_from_this(); host_->on_subscription(its_service, its_instance, its_eventgroup, @@ -1011,6 +1145,8 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, notify_remote_initially(its_service, its_instance, its_eventgroup, its_already_subscribed_events); } + send_pending_notify_ones(its_service, its_instance, its_eventgroup, its_client, true); + std::uint32_t its_count = get_remote_subscriber_count( its_service, its_instance, its_eventgroup, true); VSOMEIP_INFO << "SUBSCRIBE(" @@ -1022,19 +1158,51 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, << std::dec << (uint16_t)its_major << "]" << (bool)(its_subscription_id != DEFAULT_SUBSCRIPTION) << " " << std::dec << its_count; + + routing_manager_base::erase_incoming_subscription_state(its_client, its_service, + its_instance, its_eventgroup, its_event); }); } else if (is_client_known(its_client)) { its_lock.unlock(); - if (!configuration_->is_client_allowed(its_client, - its_service, its_instance)) { - VSOMEIP_WARNING << "Security: Client " << std::hex - << its_client << " subscribes to service/instance " - << its_service << "/" << its_instance - << " which violates the security policy ~> Skip subscribe!"; - return; + if (!message_from_routing) { + if (its_event == ANY_EVENT) { + if (!is_subscribe_to_any_event_allowed(its_client, its_service, its_instance, its_eventgroup)) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_client + << " : routing_manager_proxy::on_message: " + << " isn't allowed to subscribe to service/instance/event " + << its_service << "/" << its_instance << "/ANY_EVENT" + << " which violates the security policy ~> Skip subscribe!"; + return; + } + } else { + if (!configuration_->is_client_allowed(its_client, + its_service, its_instance, its_event)) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_client + << " : routing_manager_proxy::on_message: " + << " subscribes to service/instance/event " + << its_service << "/" << its_instance << "/" << its_event + << " which violates the security policy ~> Skip subscribe!"; + return; + } + } + } else { + if (!configuration_->is_remote_client_allowed()) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_client + << " : routing_manager_proxy::on_message: " + << std::hex << "Routing manager with client ID 0x" + << its_client + << " isn't allowed to subscribe to service/instance/event " + << its_service << "/" << its_instance + << "/" << its_event + << " respectively to client 0x" << get_client() + << " ~> Skip Subscribe!"; + return; + } } // Local & already known subscriber: create endpoint + send (N)ACK + insert subscription + routing_manager_base::set_incoming_subscription_state(its_client, its_service, + its_instance, its_eventgroup, its_event, subscription_state_e::IS_SUBSCRIBING); (void) find_or_create_local(its_client); auto self = shared_from_this(); host_->on_subscription(its_service, its_instance, @@ -1053,6 +1221,8 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, subscription_type_e::SU_RELIABLE_AND_UNRELIABLE); send_pending_notify_ones(its_service, its_instance, its_eventgroup, its_client); } + routing_manager_base::erase_incoming_subscription_state(its_client, its_service, + its_instance, its_eventgroup, its_event); }); } else { // Local & not yet known subscriber ~> set pending until subscriber gets known! @@ -1182,7 +1352,11 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, break; case VSOMEIP_OFFERED_SERVICES_RESPONSE: - if (!configuration_->is_security_enabled() ||_bound_client == routing_host_id) { + if (_size - VSOMEIP_COMMAND_HEADER_SIZE != its_length) { + VSOMEIP_WARNING << "Received a VSOMEIP_OFFERED_SERVICES_RESPONSE command with invalid size -> skip!"; + break; + } + if (!configuration_->is_security_enabled() || message_from_routing) { on_offered_services_info(&_data[VSOMEIP_COMMAND_PAYLOAD_POS], its_length); } else { VSOMEIP_WARNING << std::hex << "Security: Client 0x" << get_client() @@ -1190,7 +1364,148 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, << " : Skip message!"; } break; + case VSOMEIP_RESEND_PROVIDED_EVENTS: { + if (_size != VSOMEIP_RESEND_PROVIDED_EVENTS_COMMAND_SIZE) { + VSOMEIP_WARNING << "Received a RESEND_PROVIDED_EVENTS command with wrong size ~> skip!"; + break; + } + pending_remote_offer_id_t its_pending_remote_offer_id(0); + std::memcpy(&its_pending_remote_offer_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + sizeof(pending_remote_offer_id_t)); + resend_provided_event_registrations(); + send_resend_provided_event_response(its_pending_remote_offer_id); + VSOMEIP_INFO << "RESEND_PROVIDED_EVENTS(" + << std::hex << std::setw(4) << std::setfill('0') + << its_client << ")"; + break; + } + case VSOMEIP_UPDATE_SECURITY_POLICY: { + if (_size < VSOMEIP_COMMAND_HEADER_SIZE + sizeof(pending_security_update_id_t) || + _size - VSOMEIP_COMMAND_HEADER_SIZE != its_length) { + VSOMEIP_WARNING << "vSomeIP Security: Received a VSOMEIP_UPDATE_SECURITY_POLICY command with wrong size -> skip!"; + break; + } + if (!configuration_->is_security_enabled() || message_from_routing) { + pending_security_update_id_t its_update_id(0); + uint32_t its_uid(0); + uint32_t its_gid(0); + + std::memcpy(&its_update_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + sizeof(pending_security_update_id_t)); + + std::shared_ptr<policy> its_policy(std::make_shared<policy>()); + const byte_t* buffer_ptr = _data + (VSOMEIP_COMMAND_PAYLOAD_POS + + sizeof(pending_security_update_id_t)); + uint32_t its_size = uint32_t(_size - (VSOMEIP_COMMAND_PAYLOAD_POS + + sizeof(pending_security_update_id_t))); + utility::parse_policy(buffer_ptr, its_size, its_uid, its_gid, its_policy); + + if (configuration_->is_policy_update_allowed(its_uid, its_policy)) { + configuration_->update_security_policy(its_uid, its_gid, its_policy); + send_update_security_policy_response(its_update_id); + } + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << " received a security policy update from a client which isn't the routing manager" + << " : Skip message!"; + } + break; + } + case VSOMEIP_REMOVE_SECURITY_POLICY: { + if (_size != VSOMEIP_REMOVE_SECURITY_POLICY_COMMAND_SIZE) { + VSOMEIP_WARNING << "vSomeIP Security: Received a VSOMEIP_REMOVE_SECURITY_POLICY command with wrong size ~> skip!"; + break; + } + if (!configuration_->is_security_enabled() || message_from_routing) { + pending_security_update_id_t its_update_id(0); + uint32_t its_uid(0xffffffff); + uint32_t its_gid(0xffffffff); + + std::memcpy(&its_update_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + sizeof(pending_security_update_id_t)); + std::memcpy(&its_uid, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 4], + sizeof(uint32_t)); + std::memcpy(&its_gid, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 8], + sizeof(uint32_t)); + if (configuration_->is_policy_removal_allowed(its_uid)) { + configuration_->remove_security_policy(its_uid, its_gid); + send_remove_security_policy_response(its_update_id); + } + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << "received a security policy removal from a client which isn't the routing manager" + << " : Skip message!"; + } + break; + } + case VSOMEIP_DISTRIBUTE_SECURITY_POLICIES: { + if (_size < VSOMEIP_COMMAND_HEADER_SIZE || + _size - VSOMEIP_COMMAND_HEADER_SIZE != its_length) { + VSOMEIP_WARNING << "vSomeIP Security: Received a VSOMEIP_DISTRIBUTE_SECURITY_POLICIES command with wrong size -> skip!"; + break; + } + if (!configuration_->is_security_enabled() || message_from_routing) { + uint32_t its_policy_count(0); + uint32_t its_policy_size(0); + const byte_t* buffer_ptr = 0; + + if (VSOMEIP_COMMAND_PAYLOAD_POS + sizeof(uint32_t) * 2 <= _size) { + std::memcpy(&its_policy_count, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + sizeof(uint32_t)); + + // skip policy count field + buffer_ptr = _data + (VSOMEIP_COMMAND_PAYLOAD_POS + + sizeof(uint32_t)); + + for (uint32_t i = 0; i < its_policy_count; i++) { + uint32_t its_uid(0); + uint32_t its_gid(0); + std::shared_ptr<policy> its_policy(std::make_shared<policy>()); + // length field of next (UID/GID + policy) + if (buffer_ptr + sizeof(uint32_t) <= _data + _size) { + std::memcpy(&its_policy_size, buffer_ptr, + sizeof(uint32_t)); + buffer_ptr += sizeof(uint32_t); + + if (buffer_ptr + its_policy_size <= _data + _size) { + if (utility::parse_policy(buffer_ptr, its_policy_size, its_uid, its_gid, its_policy)) { + if (configuration_->is_policy_update_allowed(its_uid, its_policy)) { + configuration_->update_security_policy(its_uid, its_gid, its_policy); + } + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() << " could not parse policy!"; + } + } + } + } + } + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << " received a security policy distribution command from a client which isn't the routing manager" + << " : Skip message!"; + } + break; + } + case VSOMEIP_UPDATE_SECURITY_CREDENTIALS: { + if (_size < VSOMEIP_COMMAND_HEADER_SIZE || + _size - VSOMEIP_COMMAND_HEADER_SIZE != its_length) { + VSOMEIP_WARNING << "vSomeIP Security: Received a VSOMEIP_UPDATE_SECURITY_CREDENTIALS command with wrong size -> skip!"; + break; + } + if (!configuration_->is_security_enabled() || message_from_routing) { + on_update_security_credentials(&_data[VSOMEIP_COMMAND_PAYLOAD_POS], its_length); + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_message: " + << "received a security credential update from a client which isn't the routing manager" + << " : Skip message!"; + } + break; + } default: break; } @@ -1237,6 +1552,17 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, VSOMEIP_INFO << std::hex << "Application/Client " << get_client() << " is registered."; +#ifndef _WIN32 + if (!check_credentials(get_client(), getuid(), getgid())) { + VSOMEIP_ERROR << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::on_routing_info: RIE_ADD_CLIENT: isn't allowed" + << " to use the server endpoint due to credential check failed!"; + deregister_application(); + host_->on_state(static_cast<state_type_e>(inner_state_type_e::ST_DEREGISTERED)); + return; + } +#endif + // inform host about its own registration state changes host_->on_state(static_cast<state_type_e>(inner_state_type_e::ST_REGISTERED)); @@ -1257,6 +1583,7 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, known_clients_.erase(its_client); } if (its_client == get_client()) { + configuration_->remove_client_to_uid_gid_mapping(its_client); VSOMEIP_INFO << std::hex << "Application/Client " << get_client() << " is deregistered."; @@ -1270,7 +1597,7 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, state_condition_.notify_one(); } } else if (its_client != VSOMEIP_ROUTING_CLIENT) { - remove_local(its_client); + remove_local(its_client, true); } } @@ -1325,6 +1652,8 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, auto found_service = local_services_.find(its_service); if (found_service != local_services_.end()) { found_service->second.erase(its_instance); + // move previously offering client to history + local_services_history_[its_service][its_instance].insert(its_client); if (found_service->second.size() == 0) { local_services_.erase(its_service); } @@ -1374,6 +1703,8 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, } } for (const subscription_info &si : subscription_actions) { + routing_manager_base::set_incoming_subscription_state(si.client_id_, si.service_id_, si.instance_id_, + si.eventgroup_id_, si.event_, subscription_state_e::IS_SUBSCRIBING); (void) find_or_create_local(si.client_id_); auto self = shared_from_this(); host_->on_subscription( @@ -1393,6 +1724,8 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, send_pending_notify_ones(si.service_id_, si.instance_id_, si.eventgroup_id_, si.client_id_); } + routing_manager_base::erase_incoming_subscription_state(si.client_id_, si.service_id_, + si.instance_id_, si.eventgroup_id_, si.event_); { std::lock_guard<std::recursive_mutex> its_lock2(incoming_subscriptions_mutex_); pending_incoming_subscripitons_.erase(si.client_id_); @@ -1469,12 +1802,26 @@ void routing_manager_proxy::reconnect(const std::unordered_set<client_t> &_clien // Remove all local connections/endpoints for (const auto its_client : _clients) { if (its_client != VSOMEIP_ROUTING_CLIENT) { - remove_local(its_client); + remove_local(its_client, true); } } VSOMEIP_INFO << std::hex << "Application/Client " << get_client() <<": Reconnecting to routing manager."; + +#ifndef _WIN32 + if (!check_credentials(get_client(), getuid(), getgid())) { + VSOMEIP_ERROR << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_proxy::reconnect: isn't allowed" + << " to use the server endpoint due to credential check failed!"; + std::lock_guard<std::mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->stop(); + } + return; + } +#endif + std::lock_guard<std::mutex> its_lock(sender_mutex_); if (sender_) { sender_->restart(); @@ -1509,14 +1856,10 @@ void routing_manager_proxy::register_application() { } void routing_manager_proxy::deregister_application() { - uint32_t its_size = sizeof(client_); - - std::vector<byte_t> its_command(VSOMEIP_COMMAND_HEADER_SIZE + its_size); + std::vector<byte_t> its_command(VSOMEIP_COMMAND_HEADER_SIZE, 0); its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_DEREGISTER_APPLICATION; std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, sizeof(client_)); - std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); if (is_connected_) { std::lock_guard<std::mutex> its_lock(sender_mutex_); @@ -1558,7 +1901,7 @@ void routing_manager_proxy::send_request_services(std::set<service_data_t>& _req std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, sizeof(client_)); std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); + sizeof(std::uint32_t)); uint32_t entry_size = (sizeof(service_t) + sizeof(instance_t) + sizeof(major_version_t) + sizeof(minor_version_t) + sizeof(bool)); @@ -1788,17 +2131,13 @@ void routing_manager_proxy::init_receiver() { ::_unlink(its_client.str().c_str()); int port = VSOMEIP_INTERNAL_BASE_PORT + client_; #else - if (!check_credentials(get_client(), getuid(), getgid())) { - VSOMEIP_ERROR << "routing_manager_proxy::init_receiver: " - << std::hex << "Client " << get_client() << " isn't allow" - << " to create a server endpoint due to credential check failed!"; - return; - } + configuration_->store_client_to_uid_gid_mapping(get_client(), getuid(), getgid()); + configuration_->store_uid_gid_to_client_mapping(getuid(), getgid(), get_client()); + if (-1 == ::unlink(its_client.str().c_str()) && errno != ENOENT) { VSOMEIP_ERROR << "routing_manager_proxy::init_receiver unlink failed (" << its_client.str() << "): "<< std::strerror(errno); } - const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); #endif try { receiver_ = std::make_shared<local_server_endpoint_impl>(shared_from_this(), @@ -1809,7 +2148,8 @@ void routing_manager_proxy::init_receiver() { #endif io_, configuration_->get_max_message_size_local(), configuration_->get_buffer_shrink_threshold(), - configuration_->get_endpoint_queue_limit_local()); + configuration_->get_endpoint_queue_limit_local(), + configuration_->get_permissions_uds()); #ifdef _WIN32 VSOMEIP_INFO << "Listening at " << port; #else @@ -1819,9 +2159,6 @@ void routing_manager_proxy::init_receiver() { host_->on_error(error_code_e::SERVER_ENDPOINT_CREATION_FAILED); VSOMEIP_ERROR << "Client ID: " << std::hex << client_ << ": " << e.what(); } -#ifndef _WIN32 - ::umask(previous_mask); -#endif } void routing_manager_proxy::notify_remote_initially(service_t _service, instance_t _instance, @@ -2008,7 +2345,7 @@ void routing_manager_proxy::handle_client_error(client_t _client) { if (_client != VSOMEIP_ROUTING_CLIENT) { VSOMEIP_INFO << "Client 0x" << std::hex << get_client() << " handles a client error(" << std::hex << _client << ")"; - remove_local(_client); + remove_local(_client, true); } else { bool should_reconnect(true); { @@ -2077,4 +2414,99 @@ void routing_manager_proxy::send_unsubscribe_ack( } } +void routing_manager_proxy::resend_provided_event_registrations() { + std::lock_guard<std::mutex> its_lock(state_mutex_); + for (const event_data_t& ed : pending_event_registrations_) { + if (ed.is_provided_) { + send_register_event(client_, ed.service_, ed.instance_, + ed.event_, ed.eventgroups_, + ed.is_field_, ed.is_provided_); + } + } +} + +void routing_manager_proxy::send_resend_provided_event_response(pending_remote_offer_id_t _id) { + byte_t its_command[VSOMEIP_RESEND_PROVIDED_EVENTS_COMMAND_SIZE]; + const std::uint32_t its_size = VSOMEIP_RESEND_PROVIDED_EVENTS_COMMAND_SIZE + - VSOMEIP_COMMAND_HEADER_SIZE; + + const client_t its_client = get_client(); + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_RESEND_PROVIDED_EVENTS; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &its_client, + sizeof(its_client)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_id, + sizeof(pending_remote_offer_id_t)); + { + std::lock_guard<std::mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } +} + +void routing_manager_proxy::send_update_security_policy_response(pending_security_update_id_t _update_id) { + byte_t its_command[VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE]; + const std::uint32_t its_size = VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE + - VSOMEIP_COMMAND_HEADER_SIZE; + + const client_t its_client = get_client(); + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &its_client, + sizeof(its_client)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_update_id, + sizeof(pending_security_update_id_t)); + { + std::lock_guard<std::mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } +} + +void routing_manager_proxy::send_remove_security_policy_response(pending_security_update_id_t _update_id) { + byte_t its_command[VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE]; + const std::uint32_t its_size = VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE + - VSOMEIP_COMMAND_HEADER_SIZE; + + const client_t its_client = get_client(); + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &its_client, + sizeof(its_client)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_update_id, + sizeof(pending_security_update_id_t)); + { + std::lock_guard<std::mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } +} + +void routing_manager_proxy::on_update_security_credentials(const byte_t *_data, uint32_t _size) { + uint32_t i = 0; + while ( (i + sizeof(uint32_t) + sizeof(uint32_t)) <= _size) { + std::shared_ptr<policy> its_policy(std::make_shared<policy>()); + ranges_t its_uid_ranges, its_gid_ranges; + uint32_t its_uid, its_gid; + + std::memcpy(&its_uid, &_data[i], sizeof(uint32_t)); + i += uint32_t(sizeof(uint32_t)); + std::memcpy(&its_gid, &_data[i], sizeof(uint32_t)); + i += uint32_t(sizeof(uint32_t)); + + its_uid_ranges.insert(std::make_pair(its_uid, its_uid)); + its_gid_ranges.insert(std::make_pair(its_gid, its_gid)); + + its_policy->allow_who_ = true; + its_policy->ids_.insert(std::make_pair(its_uid_ranges, its_gid_ranges)); + configuration_->add_security_credentials(its_uid, its_gid, its_policy, get_client()); + } +} + } // namespace vsomeip diff --git a/implementation/routing/src/routing_manager_stub.cpp b/implementation/routing/src/routing_manager_stub.cpp index 528c729..5c19af9 100644 --- a/implementation/routing/src/routing_manager_stub.cpp +++ b/implementation/routing/src/routing_manager_stub.cpp @@ -8,12 +8,6 @@ #include <iomanip> #include <forward_list> -#ifndef _WIN32 -// for umask -#include <sys/types.h> -#include <sys/stat.h> -#endif - #include <boost/system/error_code.hpp> #include <vsomeip/constants.hpp> @@ -34,6 +28,7 @@ #include "../../logging/include/logger.hpp" #include "../../utility/include/byteorder.hpp" #include "../../utility/include/utility.hpp" +#include "../implementation/message/include/payload_impl.hpp" namespace vsomeip { @@ -162,6 +157,7 @@ const std::shared_ptr<configuration> routing_manager_stub::get_configuration() c void routing_manager_stub::on_connect(std::shared_ptr<endpoint> _endpoint) { _endpoint->set_connected(true); + _endpoint->set_established(true); } void routing_manager_stub::on_disconnect(std::shared_ptr<endpoint> _endpoint) { @@ -211,6 +207,7 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, std::string its_client_endpoint; service_t its_service; instance_t its_instance; + method_t its_method; eventgroup_t its_eventgroup; std::set<eventgroup_t> its_eventgroups; event_t its_event; @@ -235,10 +232,11 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_client)); if (configuration_->is_security_enabled() && _bound_client != its_client) { - VSOMEIP_WARNING << "Security: Routing Manager received a message from client " + VSOMEIP_WARNING << "vSomeIP Security: routing_manager_stub::on_message: " + << "Routing Manager received a message from client " << std::hex << std::setw(4) << std::setfill('0') - << its_client << " with command " << (uint32_t)its_command << - " which doesn't match the bound client " + << its_client << " with command " << (uint32_t)its_command + << " which doesn't match the bound client " << std::setw(4) << std::setfill('0') << _bound_client << " ~> skip message!"; return; @@ -250,14 +248,26 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, if (its_size <= _size - VSOMEIP_COMMAND_HEADER_SIZE) { switch (its_command) { case VSOMEIP_REGISTER_APPLICATION: + if (_size != VSOMEIP_REGISTER_APPLICATION_COMMAND_SIZE) { + VSOMEIP_WARNING << "Received a REGISTER_APPLICATION command with wrong size ~> skip!"; + break; + } update_registration(its_client, registration_type_e::REGISTER); break; case VSOMEIP_DEREGISTER_APPLICATION: + if (_size != VSOMEIP_DEREGISTER_APPLICATION_COMMAND_SIZE) { + VSOMEIP_WARNING << "Received a DEREGISTER_APPLICATION command with wrong size ~> skip!"; + break; + } update_registration(its_client, registration_type_e::DEREGISTER); break; case VSOMEIP_PONG: + if (_size != VSOMEIP_PONG_COMMAND_SIZE) { + VSOMEIP_WARNING << "Received a PONG command with wrong size ~> skip!"; + break; + } on_pong(its_client); VSOMEIP_TRACE << "PONG(" << std::hex << std::setw(4) << std::setfill('0') << its_client << ")"; @@ -277,8 +287,16 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_major)); std::memcpy(&its_minor, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 5], sizeof(its_minor)); - host_->offer_service(its_client, its_service, its_instance, - its_major, its_minor); + + if (configuration_->is_offer_allowed(its_client, its_service, its_instance)) { + host_->offer_service(its_client, its_service, its_instance, + its_major, its_minor); + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_client + << " : routing_manager_stub::on_message: isn't allowed to offer " + << "the following service/instance " << its_service << "/" << its_instance + << " ~> Skip offer!"; + } break; case VSOMEIP_STOP_OFFER_SERVICE: @@ -317,15 +335,30 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_event)); std::memcpy(&its_subscription_type, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 9], sizeof(its_subscription_type)); - if (configuration_->is_client_allowed(its_client, - its_service, its_instance)) { - host_->subscribe(its_client, its_service, its_instance, - its_eventgroup, its_major, its_event, its_subscription_type); + + if (its_event == ANY_EVENT) { + if (host_->is_subscribe_to_any_event_allowed(its_client, its_service, its_instance, its_eventgroup)) { + host_->subscribe(its_client, its_service, its_instance, + its_eventgroup, its_major, its_event, its_subscription_type); + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_client + << " : routing_manager_stub::on_message: " + << " subscribes to service/instance/event " + << its_service << "/" << its_instance << "/ANY_EVENT" + << " which violates the security policy ~> Skip subscribe!"; + } } else { - VSOMEIP_WARNING << "Security: Client " << std::hex - << its_client << " subscribes to service/instance " - << its_service << "/" << its_instance - << " which violates the security policy ~> Skip subscribe!"; + if (configuration_->is_client_allowed(its_client, + its_service, its_instance, its_event)) { + host_->subscribe(its_client, its_service, its_instance, + its_eventgroup, its_major, its_event, its_subscription_type); + } else { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_client + << " : routing_manager_stub::on_message: " + << " subscribes to service/instance/event " + << its_service << "/" << its_instance << "/" << its_event + << " which violates the security policy ~> Skip subscribe!"; + } } break; @@ -422,6 +455,10 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, << std::hex << std::setw(4) << std::setfill('0') << its_eventgroup << "]"; break; case VSOMEIP_SEND: { + if (_size < VSOMEIP_SEND_COMMAND_SIZE + VSOMEIP_FULL_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a SEND command with too small size ~> skip!"; + break; + } its_data = &_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS]; its_service = VSOMEIP_BYTES_TO_WORD( its_data[VSOMEIP_SERVICE_POS_MIN], @@ -429,6 +466,9 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, its_client_from_header = VSOMEIP_BYTES_TO_WORD( its_data[VSOMEIP_CLIENT_POS_MIN], its_data[VSOMEIP_CLIENT_POS_MAX]); + its_method = VSOMEIP_BYTES_TO_WORD( + its_data[VSOMEIP_METHOD_POS_MIN], + its_data[VSOMEIP_METHOD_POS_MAX]); std::memcpy(&its_instance, &_data[VSOMEIP_SEND_COMMAND_INSTANCE_POS_MIN], sizeof(its_instance)); std::memcpy(&its_reliable, &_data[VSOMEIP_SEND_COMMAND_RELIABLE_POS], @@ -436,34 +476,40 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, std::memcpy(&its_is_valid_crc, &_data[VSOMEIP_SEND_COMMAND_VALID_CRC_POS], sizeof(its_is_valid_crc)); + // Allow response messages from local proxies as answer to remote requests + // but check requests sent by local proxies to remote against policy. if (utility::is_request(its_data[VSOMEIP_MESSAGE_TYPE_POS])) { if (!configuration_->is_client_allowed(its_client_from_header, - its_service, its_instance)) { - VSOMEIP_WARNING << std::hex << "Security: Client 0x" << its_client_from_header - << " isn't allow to send a request to service/instance " - << its_service << "/" << its_instance - << " : Skip message!"; - return; - } - } else { - if (!configuration_->is_client_allowed(get_client(), its_service, - its_instance)) { - VSOMEIP_WARNING << std::hex << "Security: Client 0x" - << get_client() - << " isn't allow receive a response from to service/instance " - << its_service << "/" << its_instance - << " respectively from client 0x" << its_client - << " : Skip message!"; + its_service, its_instance, its_method)) { + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << its_client_from_header + << " : routing_manager_stub::on_message: " + << " isn't allowed to send a request to service/instance/method " + << its_service << "/" << its_instance << "/" << its_method + << " ~> Skip message!"; return; } } // reduce by size of instance, flush, reliable, client and is_valid_crc flag const std::uint32_t its_message_size = its_size - (VSOMEIP_SEND_COMMAND_SIZE - VSOMEIP_COMMAND_HEADER_SIZE); - host_->on_message(its_service, its_instance, its_data, its_message_size, its_reliable, its_is_valid_crc); + if (its_message_size != + VSOMEIP_BYTES_TO_LONG(_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 1], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 2], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 3]) + + VSOMEIP_SOMEIP_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a SEND command containing message with invalid size -> skip!"; + break; + } + host_->on_message(its_service, its_instance, its_data, its_message_size, + its_reliable, _bound_client, its_is_valid_crc, false); break; } case VSOMEIP_NOTIFY: { + if (_size < VSOMEIP_SEND_COMMAND_SIZE + VSOMEIP_FULL_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a NOTIFY command with too small size ~> skip!"; + break; + } its_data = &_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS]; its_service = VSOMEIP_BYTES_TO_WORD( its_data[VSOMEIP_SERVICE_POS_MIN], @@ -473,10 +519,23 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, // reduce by size of instance, flush, reliable, is_valid_crc flag and target client const std::uint32_t its_message_size = its_size - (VSOMEIP_SEND_COMMAND_SIZE - VSOMEIP_COMMAND_HEADER_SIZE); + if (its_message_size != + VSOMEIP_BYTES_TO_LONG(_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 1], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 2], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 3]) + + VSOMEIP_SOMEIP_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a NOTIFY command containing message with invalid size -> skip!"; + break; + } host_->on_notification(VSOMEIP_ROUTING_CLIENT, its_service, its_instance, its_data, its_message_size); break; } case VSOMEIP_NOTIFY_ONE: { + if (_size < VSOMEIP_SEND_COMMAND_SIZE + VSOMEIP_FULL_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a NOTIFY_ONE command with too small size ~> skip!"; + break; + } its_data = &_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS]; its_service = VSOMEIP_BYTES_TO_WORD( its_data[VSOMEIP_SERVICE_POS_MIN], @@ -488,6 +547,15 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, // reduce by size of instance, flush, reliable flag, is_valid_crc and target client const std::uint32_t its_message_size = its_size - (VSOMEIP_SEND_COMMAND_SIZE - VSOMEIP_COMMAND_HEADER_SIZE); + if (its_message_size != + VSOMEIP_BYTES_TO_LONG(_data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 1], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 2], + _data[VSOMEIP_SEND_COMMAND_PAYLOAD_POS + VSOMEIP_LENGTH_POS_MIN + 3]) + + VSOMEIP_SOMEIP_HEADER_SIZE) { + VSOMEIP_WARNING << "Received a NOTIFY_ONE command containing message with invalid size -> skip!"; + break; + } host_->on_notification(its_target_client, its_service, its_instance, its_data, its_message_size, true); break; @@ -496,6 +564,10 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, { uint32_t entry_size = (sizeof(service_t) + sizeof(instance_t) + sizeof(major_version_t) + sizeof(minor_version_t) + sizeof(bool)); + if (its_size % entry_size > 0) { + VSOMEIP_WARNING << "Received a REQUEST_SERVICE command with invalid size -> skip!"; + break; + } uint32_t request_count(its_size / entry_size); std::set<service_data_t> requests; for (uint32_t i = 0; i < request_count; ++i) { @@ -515,19 +587,23 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_minor)); std::memcpy(&use_exclusive_proxy, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 9 + (i * entry_size)], sizeof(use_exclusive_proxy)); - if (configuration_->is_client_allowed(its_client, its_service, its_instance)) { + if (configuration_->is_client_allowed(its_client, its_service, its_instance, 0x00, true)) { host_->request_service(its_client, its_service, its_instance, its_major, its_minor, use_exclusive_proxy); service_data_t request = { its_service, its_instance, its_major, its_minor, use_exclusive_proxy }; requests.insert(request); } else { - VSOMEIP_WARNING << "Security: Client " << std::hex - << its_client << " requests service/instance " + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex + << its_client << " : routing_manager_stub::on_message: " + << "requests service/instance " << its_service << "/" << its_instance << " which violates the security policy ~> Skip request!"; } } + if (configuration_->is_security_enabled()) { + handle_credentials(its_client, requests); + } handle_requests(its_client, requests); break; } @@ -640,10 +716,14 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, break; case VSOMEIP_REGISTERED_ACK: + if (_size != VSOMEIP_REGISTERED_ACK_COMMAND_SIZE) { + VSOMEIP_WARNING << "Received a REGISTERED_ACK command with wrong size ~> skip!"; + break; + } VSOMEIP_INFO << "REGISTERED_ACK(" << std::hex << std::setw(4) << std::setfill('0') << its_client << ")"; break; - case VSOMEIP_OFFERED_SERVICES_REQUEST: + case VSOMEIP_OFFERED_SERVICES_REQUEST: { if (_size != VSOMEIP_OFFERED_SERVICES_COMMAND_SIZE) { VSOMEIP_WARNING << "Received a VSOMEIP_OFFERED_SERVICES_REQUEST command with wrong size ~> skip!"; break; @@ -694,6 +774,46 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, } send_offered_services_info(its_client); break; + } + case VSOMEIP_RESEND_PROVIDED_EVENTS: { + if (_size != VSOMEIP_RESEND_PROVIDED_EVENTS_COMMAND_SIZE) { + VSOMEIP_WARNING << "Received a RESEND_PROVIDED_EVENTS command with wrong size ~> skip!"; + break; + } + pending_remote_offer_id_t its_pending_remote_offer_id(0); + std::memcpy(&its_pending_remote_offer_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + sizeof(pending_remote_offer_id_t)); + host_->on_resend_provided_events_response(its_pending_remote_offer_id); + VSOMEIP_INFO << "RESEND_PROVIDED_EVENTS(" + << std::hex << std::setw(4) << std::setfill('0') << its_client << ")"; + break; + } + case VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE: { + if (_size != VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE) { + VSOMEIP_WARNING << "vSomeIP Security: Received a VSOMEIP_UPDATE_SECURITY_POLICY_RESPONSE " + << "command with wrong size ~> skip!"; + break; + } + pending_security_update_id_t its_pending_security_update_id(0); + std::memcpy(&its_pending_security_update_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + sizeof(pending_security_update_id_t)); + + host_->on_security_update_response(its_pending_security_update_id ,its_client); + break; + } + case VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE: { + if (_size != VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE_COMMAND_SIZE) { + VSOMEIP_WARNING << "vSomeIP Security: Received a VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE " + << "command with wrong size ~> skip!"; + break; + } + pending_security_update_id_t its_pending_security_update_id(0); + std::memcpy(&its_pending_security_update_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], + sizeof(pending_security_update_id_t)); + + host_->on_security_update_response(its_pending_security_update_id ,its_client); + break; + } } } } @@ -778,6 +898,10 @@ void routing_manager_stub::client_registration_func(void) { routing_info_entry_e::RIE_ADD_CLIENT : routing_info_entry_e::RIE_DEL_CLIENT, r.first); + // distribute updated security config to new clients + if (b == registration_type_e::REGISTER) { + send_cached_security_policies(r.first); + } send_client_routing_info(r.first); } if (b != registration_type_e::REGISTER) { @@ -802,7 +926,9 @@ void routing_manager_stub::client_registration_func(void) { } service_requests_.erase(r.first); } - host_->remove_local(r.first); + // Don't remove client ID to UID maping as same client + // could have passed its credentials again + host_->remove_local(r.first, false); if (b == registration_type_e::DEREGISTER_ON_ERROR) { utility::release_client_id(r.first); } @@ -819,8 +945,7 @@ void routing_manager_stub::init_routing_endpoint() { endpoint_path_ = its_endpoint_path.str(); client_t routing_host_id = configuration_->get_id(configuration_->get_routing_host()); if (configuration_->is_security_enabled() && get_client() != routing_host_id) { - VSOMEIP_ERROR << "routing_manager_stub::init_routing_endpoint: " - << std::hex << "Client " << get_client() << " isn't allow" + VSOMEIP_ERROR << __func__ << std::hex << " Client " << get_client() << " isn't allowed" << " to create the routing endpoint due to its not configured as the routing master!"; return; } @@ -840,7 +965,9 @@ void routing_manager_stub::init_routing_endpoint() { > (shared_from_this(), boost::asio::local::stream_protocol::endpoint(endpoint_path_), io_, configuration_->get_max_message_size_local(), native_socket_fd, - configuration_->get_buffer_shrink_threshold()); + configuration_->get_buffer_shrink_threshold(), + configuration_->get_endpoint_queue_limit_local(), + configuration_->get_permissions_uds()); } catch (const std::exception &e) { VSOMEIP_ERROR << ERROR_INFO[static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED)] << " (" << static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED) << ")"; @@ -860,8 +987,6 @@ void routing_manager_stub::init_routing_endpoint() { << endpoint_path_ << "): "<< std::strerror(errno); } VSOMEIP_INFO << "init_routing_endpoint Routing endpoint at " << endpoint_path_; - - const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); #endif try { @@ -875,16 +1000,14 @@ void routing_manager_stub::init_routing_endpoint() { #endif io_, configuration_->get_max_message_size_local(), configuration_->get_buffer_shrink_threshold(), - configuration_->get_endpoint_queue_limit_local()); + configuration_->get_endpoint_queue_limit_local(), + configuration_->get_permissions_uds()); } catch (const std::exception &e) { VSOMEIP_ERROR << ERROR_INFO[static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED)] << " (" << static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED) << ")"; VSOMEIP_ERROR << "routing_manager_stub::init_routing_endpoint Client ID: " << std::hex << VSOMEIP_ROUTING_CLIENT << ": " << e.what(); } - #ifndef _WIN32 - ::umask(previous_mask); - #endif is_socket_activated_ = false; } } @@ -899,13 +1022,17 @@ void routing_manager_stub::on_offer_service(client_t _client, configuration_->is_offer_allowed(_client, _service, _instance)) { std::lock_guard<std::mutex> its_guard(routing_info_mutex_); routing_info_[_client].second[_service][_instance] = std::make_pair(_major, _minor); + if (configuration_->is_security_enabled()) { + distribute_credentials(_client, _service, _instance); + } inform_requesters(_client, _service, _instance, _major, _minor, routing_info_entry_e::RIE_ADD_SERVICE_INSTANCE, true); } else { - VSOMEIP_WARNING << std::hex << "Security: Client 0x" << _client - << " isn't allow to offer the following service/instance " + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << _client + << " : routing_manager_stub::on_offer_service: " + << "isn't allowed to offer the following service/instance " << _service << "/" << _instance - << " : Skip offer!"; + << " ~> Skip offer!"; } } @@ -959,6 +1086,87 @@ void routing_manager_stub::create_client_routing_info(const client_t _target) { client_routing_info_[_target] = its_command; } +void routing_manager_stub::create_client_credentials_info(const client_t _target) { + std::vector<byte_t> its_command; + its_command.push_back(VSOMEIP_UPDATE_SECURITY_CREDENTIALS); + + // Sender client + client_t client = get_client(); + for (uint32_t i = 0; i < sizeof(client_t); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&client)[i]); + } + + // Overall size placeholder + byte_t size_placeholder = 0x0; + for (uint32_t i = 0; i < sizeof(uint32_t); ++i) { + its_command.push_back(size_placeholder); + } + + client_credentials_info_[_target] = its_command; +} + +void routing_manager_stub::insert_client_credentials_info(client_t _target, std::set<std::pair<uint32_t, uint32_t>> _credentials) { + if (client_credentials_info_.find(_target) == client_credentials_info_.end()) { + return; + } + + auto its_command = client_credentials_info_[_target]; + + // insert uid / gid credential pairs + for (auto its_credentials : _credentials) { + //uid + for (uint32_t i = 0; i < sizeof(uint32_t); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&std::get<0>(its_credentials))[i]); + } + //gid + for (uint32_t i = 0; i < sizeof(uint32_t); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&std::get<1>(its_credentials))[i]); + } + } + + client_credentials_info_[_target] = its_command; +} + +void routing_manager_stub::send_client_credentials_info(const client_t _target) { + if (client_credentials_info_.find(_target) == client_credentials_info_.end()) { + return; + } + + std::shared_ptr<endpoint> its_endpoint = host_->find_local(_target); + if (its_endpoint) { + auto its_command = client_credentials_info_[_target]; + + // File overall size + std::size_t its_size = its_command.size() - VSOMEIP_COMMAND_PAYLOAD_POS; + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, sizeof(uint32_t)); + its_size += VSOMEIP_COMMAND_PAYLOAD_POS; + +#if 0 + std::stringstream msg; + msg << "rms::send_credentials_info to (" << std::hex << _target << "): "; + for (uint32_t i = 0; i < its_size; ++i) + msg << std::hex << std::setw(2) << std::setfill('0') << (int)its_command[i] << " "; + VSOMEIP_INFO << msg.str(); +#endif + + // Send routing info or error! + if(its_command.size() <= max_local_message_size_ + || VSOMEIP_MAX_LOCAL_MESSAGE_SIZE == 0) { + its_endpoint->send(&its_command[0], uint32_t(its_size), true); + } else { + VSOMEIP_ERROR << "Credentials info exceeds maximum message size: Can't send!"; + } + + client_credentials_info_.erase(_target); + } else { + VSOMEIP_ERROR << "Send credentials info to client 0x" << std::hex << _target + << " failed: No valid endpoint!"; + } +} + void routing_manager_stub::create_offered_services_info(const client_t _target) { std::vector<byte_t> its_command; its_command.push_back(VSOMEIP_OFFERED_SERVICES_RESPONSE); @@ -1175,6 +1383,44 @@ void routing_manager_stub::insert_offered_services_info(client_t _target, offered_services_info_[_target] = its_command; } +void routing_manager_stub::distribute_credentials(client_t _hoster, service_t _service, instance_t _instance) { + std::set<std::pair<uint32_t, uint32_t>> its_credentials; + std::set<client_t> its_requesting_clients; + // search for clients which shall receive the credentials + for (auto its_requesting_client : service_requests_) { + auto its_service = its_requesting_client.second.find(_service); + if (its_service != its_requesting_client.second.end()) { + for (auto its_instance : its_service->second) { + if (its_instance.first == ANY_INSTANCE || + its_instance.first == _instance) { + its_requesting_clients.insert(its_requesting_client.first); + } else { + auto found_instance = its_service->second.find(_instance); + if (found_instance != its_service->second.end()) { + its_requesting_clients.insert(its_requesting_client.first); + } + } + } + } + } + + // search for UID / GID linked with the client ID that offers the requested services + std::pair<uint32_t, uint32_t> its_uid_gid; + if (configuration_->get_client_to_uid_gid_mapping(_hoster, its_uid_gid)) { + for (auto its_requesting_client : its_requesting_clients) { + std::pair<uint32_t, uint32_t> its_requester_uid_gid; + if (configuration_->get_client_to_uid_gid_mapping(its_requesting_client, its_requester_uid_gid)) { + if (its_uid_gid != its_requester_uid_gid) { + its_credentials.insert(std::make_pair(std::get<0>(its_uid_gid), std::get<1>(its_uid_gid))); + create_client_credentials_info(its_requesting_client); + insert_client_credentials_info(its_requesting_client, its_credentials); + send_client_credentials_info(its_requesting_client); + } + } + } + } +} + void routing_manager_stub::inform_requesters(client_t _hoster, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor, routing_info_entry_e _entry, bool _inform_service) { @@ -1414,7 +1660,7 @@ void routing_manager_stub::on_pong(client_t _client) { void routing_manager_stub::start_watchdog() { - std::function<void(boost::system::error_code const &)> its_callback = + auto its_callback = [this](boost::system::error_code const &_error) { if (!_error) check_watchdog(); @@ -1440,8 +1686,7 @@ void routing_manager_stub::check_watchdog() { broadcast_ping(); - std::function<void(boost::system::error_code const &)> its_callback = - [this](boost::system::error_code const &_error) { + auto its_callback = [this](boost::system::error_code const &_error) { (void)_error; std::list< client_t > lost; { @@ -1483,8 +1728,8 @@ void routing_manager_stub::create_local_receiver() { int port = VSOMEIP_INTERNAL_BASE_PORT; #else if (!check_credentials(get_client(), getuid(), getgid())) { - VSOMEIP_ERROR << "routing_manager_stub::create_local_receiver: " - << std::hex << "Client " << get_client() << " isn't allow" + VSOMEIP_ERROR << "vSomeIP Security: Client 0x" << std::hex << get_client() + << " : routing_manager_stub::create_local_receiver: isn't allowed" << " to create a server endpoint due to credential check failed!"; return; } @@ -1492,8 +1737,6 @@ void routing_manager_stub::create_local_receiver() { VSOMEIP_ERROR << "routing_manager_stub::create_local_receiver unlink (local receiver) failed (" << local_receiver_path_ << "): "<< std::strerror(errno); } - - const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); #endif try { local_receiver_ = @@ -1506,16 +1749,14 @@ void routing_manager_stub::create_local_receiver() { #endif io_, configuration_->get_max_message_size_local(), configuration_->get_buffer_shrink_threshold(), - configuration_->get_endpoint_queue_limit_local()); + configuration_->get_endpoint_queue_limit_local(), + configuration_->get_permissions_uds()); } catch (const std::exception &e) { VSOMEIP_ERROR << ERROR_INFO[static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED)] << " (" << static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED) << ")"; VSOMEIP_ERROR << "routing_manager_stub::_local_receiver Client ID: " << std::hex << VSOMEIP_ROUTING_CLIENT << ": " << e.what(); } -#ifndef _WIN32 - ::umask(previous_mask); -#endif local_receiver_->start(); } @@ -1680,6 +1921,10 @@ void routing_manager_stub::update_registration(client_t _client, << (_type == registration_type_e::REGISTER ? "registering." : "deregistering."); + if (_type != registration_type_e::REGISTER) { + configuration_->remove_client_to_uid_gid_mapping(_client); + } + if (_type == registration_type_e::DEREGISTER) { // If we receive a DEREGISTER client command // the endpoint error handler is not longer needed @@ -1715,6 +1960,46 @@ client_t routing_manager_stub::get_client() const { return host_->get_client(); } +void routing_manager_stub::handle_credentials(const client_t _client, std::set<service_data_t>& _requests) { + if (!_requests.size()) { + return; + } + + std::lock_guard<std::mutex> its_guard(routing_info_mutex_); + std::set<std::pair<uint32_t, uint32_t>> its_credentials; + std::pair<uint32_t, uint32_t> its_requester_uid_gid; + if (configuration_->get_client_to_uid_gid_mapping(_client, its_requester_uid_gid)) { + // determine credentials of offering clients using current routing info + std::set<client_t> its_offering_clients; + + // search in local clients for the offering client + for (auto request : _requests) { + std::set<client_t> its_clients; + its_clients = host_->find_local_clients(request.service_, request.instance_); + for (auto its_client : its_clients) { + its_offering_clients.insert(its_client); + } + } + + // search for UID / GID linked with the client ID that offers the requested services + for (auto its_offering_client : its_offering_clients) { + std::pair<uint32_t, uint32_t> its_uid_gid; + if (configuration_->get_client_to_uid_gid_mapping(its_offering_client, its_uid_gid)) { + if (its_uid_gid != its_requester_uid_gid) { + its_credentials.insert(std::make_pair(std::get<0>(its_uid_gid), std::get<1>(its_uid_gid))); + } + } + } + + // send credentials to clients + if (!its_credentials.empty()) { + create_client_credentials_info(_client); + insert_client_credentials_info(_client, its_credentials); + send_client_credentials_info(_client); + } + } +} + void routing_manager_stub::handle_requests(const client_t _client, std::set<service_data_t>& _requests) { if (!_requests.size()) { return; @@ -1861,4 +2146,176 @@ void routing_manager_stub::print_endpoint_status() const { } } +bool routing_manager_stub::send_provided_event_resend_request(client_t _client, + pending_remote_offer_id_t _id) { + std::shared_ptr<endpoint> its_endpoint = host_->find_local(_client); + if (its_endpoint) { + byte_t its_command[VSOMEIP_RESEND_PROVIDED_EVENTS_COMMAND_SIZE]; + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_RESEND_PROVIDED_EVENTS; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &_client, + sizeof(client_t)); + std::uint32_t its_size = sizeof(pending_remote_offer_id_t); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_id, + sizeof(pending_remote_offer_id_t)); + return its_endpoint->send(its_command, sizeof(its_command)); + } else { + VSOMEIP_WARNING << __func__ << " Couldn't send provided event resend " + "request to local client: 0x" + << std::hex << std::setw(4) << std::setfill('0') << _client; + return false; + } +} + +bool routing_manager_stub::is_policy_cached(uint32_t _uid) { + { + std::lock_guard<std::mutex> its_lock(updated_security_policies_mutex_); + if (updated_security_policies_.find(_uid) + != updated_security_policies_.end()) { + VSOMEIP_INFO << __func__ << " Policy for UID: " << std::dec + << _uid << " was already updated before!"; + return true; + } else { + return false; + } + } +} + +void routing_manager_stub::policy_cache_add(uint32_t _uid, std::shared_ptr<payload> _payload) { + // cache security policy payload for later distribution to new registering clients + { + std::lock_guard<std::mutex> its_lock(updated_security_policies_mutex_); + updated_security_policies_[_uid] = _payload; + } +} + +void routing_manager_stub::policy_cache_remove(uint32_t _uid) { + { + std::lock_guard<std::mutex> its_lock(updated_security_policies_mutex_); + updated_security_policies_.erase(_uid); + } +} + +bool routing_manager_stub::send_update_security_policy_request(client_t _client, pending_security_update_id_t _update_id, + uint32_t _uid, std::shared_ptr<payload> _payload) { + std::shared_ptr<endpoint> its_endpoint = host_->find_local(_client); + if (its_endpoint) { + std::vector<byte_t> its_command; + // command + its_command.push_back(VSOMEIP_UPDATE_SECURITY_POLICY); + + // client ID + for (uint32_t i = 0; i < sizeof(client_t); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&_client)[i]); + } + // security update id length + payload length including gid and uid + std::uint32_t its_size = uint32_t(sizeof(pending_security_update_id_t) + _payload->get_length()); + for (uint32_t i = 0; i < sizeof(its_size); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&its_size)[i]); + } + // ID of update request + for (uint32_t i = 0; i < sizeof(pending_security_update_id_t); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&_update_id)[i]); + } + // payload + for (uint32_t i = 0; i < _payload->get_length(); ++i) { + its_command.push_back(_payload->get_data()[i]); + } + + return its_endpoint->send(its_command.data(), uint32_t(its_command.size())); + } else { + return false; + } +} + +bool routing_manager_stub::send_cached_security_policies(client_t _client) { + std::vector<byte_t> its_command; + std::size_t its_size(0); + + std::lock_guard<std::mutex> its_lock(updated_security_policies_mutex_); + uint32_t its_policy_count = uint32_t(updated_security_policies_.size()); + + if (!its_policy_count) { + return true; + } + + VSOMEIP_INFO << __func__ << " Distributing [" + << std::dec << its_policy_count + << "] security policy updates to registering client: " + << std::hex << _client; + + // command + its_command.push_back(VSOMEIP_DISTRIBUTE_SECURITY_POLICIES); + + // client ID + client_t its_client = get_client(); + for (uint32_t i = 0; i < sizeof(client_t); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&its_client)[i]); + } + + //overall size (placeholder + for (uint32_t i = 0; i < sizeof(uint32_t); ++i) { + its_command.push_back(0x00); + } + + // number of policies contained in message + for (uint32_t i = 0; i < sizeof(its_policy_count); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&its_policy_count)[i]); + } + + for (auto its_uid_gid : updated_security_policies_) { + // policy payload length including gid and uid + std::uint32_t its_length = uint32_t(its_uid_gid.second->get_length()); + for (uint32_t i = 0; i < sizeof(its_length); ++i) { + its_command.push_back( + reinterpret_cast<const byte_t*>(&its_length)[i]); + } + // payload + its_command.insert(its_command.end(), its_uid_gid.second->get_data(), + its_uid_gid.second->get_data() + its_uid_gid.second->get_length()); + } + // File overall size + its_size = its_command.size() - VSOMEIP_COMMAND_PAYLOAD_POS; + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, sizeof(uint32_t)); + + std::shared_ptr<endpoint> its_endpoint = host_->find_local(_client); + if (its_endpoint) { + return its_endpoint->send(its_command.data(), uint32_t(its_command.size())); + } else { + VSOMEIP_WARNING << __func__ << " Couldn't send cached security policies " + " to registering client: 0x" + << std::hex << std::setw(4) << std::setfill('0') << _client; + return false; + } +} + +bool routing_manager_stub::send_remove_security_policy_request( client_t _client, pending_security_update_id_t _update_id, + uint32_t _uid, uint32_t _gid) { + std::shared_ptr<endpoint> its_endpoint = host_->find_local(_client); + if (its_endpoint) { + byte_t its_command[VSOMEIP_REMOVE_SECURITY_POLICY_COMMAND_SIZE]; + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_REMOVE_SECURITY_POLICY; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &_client, + sizeof(client_t)); + std::uint32_t its_size = sizeof(_update_id) + sizeof(_uid) + sizeof(_gid); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_update_id, + sizeof(uint32_t)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_uid, + sizeof(uint32_t)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 8], &_gid, + sizeof(uint32_t)); + return its_endpoint->send(its_command, sizeof(its_command)); + } else { + return false; + } +} + } // namespace vsomeip diff --git a/implementation/routing/src/serviceinfo.cpp b/implementation/routing/src/serviceinfo.cpp index 3afc937..42db53b 100644 --- a/implementation/routing/src/serviceinfo.cpp +++ b/implementation/routing/src/serviceinfo.cpp @@ -22,6 +22,18 @@ serviceinfo::serviceinfo(major_version_t _major, minor_version_t _minor, ttl_ = std::chrono::duration_cast<std::chrono::milliseconds>(ttl); } +serviceinfo::serviceinfo(const serviceinfo& _other) : + group_(_other.group_), + major_(_other.major_), + minor_(_other.minor_), + ttl_(_other.ttl_), + reliable_(_other.reliable_), + unreliable_(_other.unreliable_), + requesters_(_other.requesters_), + is_local_(_other.is_local_), + is_in_mainphase_(_other.is_in_mainphase_) + {} + serviceinfo::~serviceinfo() { } diff --git a/implementation/runtime/include/application_impl.hpp b/implementation/runtime/include/application_impl.hpp index efd7d7d..fe23c90 100644 --- a/implementation/runtime/include/application_impl.hpp +++ b/implementation/runtime/include/application_impl.hpp @@ -61,7 +61,7 @@ public: instance_t _instance, event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field, - std::chrono::milliseconds _cycle, bool _change_resets_cycle_, + std::chrono::milliseconds _cycle, bool _change_resets_cycle, const epsilon_change_func_t &_epsilon_change_func); VSOMEIP_EXPORT void stop_offer_event(service_t _service, instance_t _instance, event_t _event); @@ -184,6 +184,31 @@ public: VSOMEIP_EXPORT void register_async_subscription_handler(service_t _service, instance_t _instance, eventgroup_t _eventgroup, async_subscription_handler_t _handler); + VSOMEIP_EXPORT void set_offer_acceptance_required(ip_address_t _address, const std::string _path, bool _enable); + VSOMEIP_EXPORT offer_acceptance_map_type_t get_offer_acceptance_required(); + + VSOMEIP_EXPORT void register_offer_acceptance_handler(offer_acceptance_handler_t _handler); + + VSOMEIP_EXPORT void register_reboot_notification_handler(reboot_notification_handler_t _handler); + + VSOMEIP_EXPORT void register_routing_ready_handler(routing_ready_handler_t _handler); + VSOMEIP_EXPORT void register_routing_state_handler(routing_state_handler_t _handler); + + VSOMEIP_EXPORT bool update_service_configuration(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled, + bool _offer); + + VSOMEIP_EXPORT void update_security_policy_configuration(uint32_t _uid, + uint32_t _gid, + std::shared_ptr<policy> _policy, + std::shared_ptr<payload> _payload, + security_update_handler_t _handler); + VSOMEIP_EXPORT void remove_security_policy_configuration(uint32_t _uid, + uint32_t _gid, + security_update_handler_t _handler); private: // // Types diff --git a/implementation/runtime/src/application_impl.cpp b/implementation/runtime/src/application_impl.cpp index 219c8f0..a3040c3 100644 --- a/implementation/runtime/src/application_impl.cpp +++ b/implementation/runtime/src/application_impl.cpp @@ -27,8 +27,7 @@ #include "../../routing/include/routing_manager_impl.hpp" #include "../../routing/include/routing_manager_proxy.hpp" #include "../../utility/include/utility.hpp" -#include "../../tracing/include/trace_connector.hpp" -#include "../../tracing/include/enumeration_types.hpp" +#include "../../tracing/include/connector_impl.hpp" #include "../../plugin/include/plugin_manager.hpp" #include "../../endpoints/include/endpoint.hpp" @@ -63,6 +62,37 @@ application_impl::application_impl(const std::string &_name) application_impl::~application_impl() { runtime_->remove_application(name_); + try { + if (stop_thread_.joinable()) { + stop_thread_.detach(); + } + } catch (const std::exception& e) { + std::cerr << __func__ << " catched exception (shutdown): " << e.what() << std::endl; + } + + try { + std::lock_guard<std::mutex> its_lock_start_stop(start_stop_mutex_); + for (auto t : io_threads_) { + if (t->joinable()) { + t->detach(); + } + } + io_threads_.clear(); + } catch (const std::exception& e) { + std::cerr << __func__ << " catched exception (io threads): " << e.what() << std::endl; + } + + try { + std::lock_guard<std::mutex> its_lock(dispatcher_mutex_); + for (auto its_dispatcher : dispatchers_) { + if (its_dispatcher.second->joinable()) { + its_dispatcher.second->detach(); + } + } + dispatchers_.clear(); + } catch (const std::exception& e) { + std::cerr << __func__ << " catched exception (dispatchers): " << e.what() << std::endl; + } } void application_impl::set_configuration( @@ -102,7 +132,7 @@ bool application_impl::init() { configuration_->load(name_); VSOMEIP_INFO << "Default configuration module loaded."; } else { - std::cerr << "Service Discovery module could not be loaded!" << std::endl; + std::cerr << "Configuration module could not be loaded!" << std::endl; std::exit(EXIT_FAILURE); } } @@ -170,14 +200,22 @@ bool application_impl::init() { if (!utility::auto_configuration_init(its_configuration)) { VSOMEIP_WARNING << "Could _not_ initialize auto-configuration:" " Cannot guarantee unique application identifiers!"; + if (client_ == ILLEGAL_CLIENT) { + VSOMEIP_ERROR << "Couldn't acquire client identifier."; + return false; + } } else { // Client Identifier client_t its_old_client = client_; client_ = utility::request_client_id(its_configuration, name_, client_); if (client_ == ILLEGAL_CLIENT) { - VSOMEIP_ERROR << "Couldn't acquire client identifier"; + VSOMEIP_ERROR << "Couldn't acquire client identifier from auto-configuration."; return false; } + std::string credentials = ""; +#ifndef _WIN32 + credentials = " and UID/GID=" + std::to_string(getuid()) + "/" + std::to_string(getgid()); +#endif VSOMEIP_INFO << "SOME/IP client identifier configured. " << "Using " << std::hex << std::setfill('0') << std::setw(4) @@ -185,7 +223,7 @@ bool application_impl::init() { << " (was: " << std::hex << std::setfill('0') << std::setw(4) << its_old_client - << ")"; + << ")" << credentials; // Routing if (its_routing_host == "") { @@ -213,47 +251,16 @@ bool application_impl::init() { #ifdef USE_DLT // Tracing - std::shared_ptr<tc::trace_connector> its_trace_connector = tc::trace_connector::get(); - std::shared_ptr<cfg::trace> its_trace_cfg = its_configuration->get_trace(); - - auto &its_channels_cfg = its_trace_cfg->channels_; - for (auto it = its_channels_cfg.begin(); it != its_channels_cfg.end(); ++it) { - its_trace_connector->add_channel(it->get()->id_, it->get()->name_); - } - - auto &its_filter_rules_cfg = its_trace_cfg->filter_rules_; - for (auto it = its_filter_rules_cfg.begin(); it != its_filter_rules_cfg.end(); ++it) { - std::shared_ptr<cfg::trace_filter_rule> its_filter_rule_cfg = *it; - tc::trace_connector::filter_rule_t its_filter_rule; - tc::filter_type_e its_filter_type; - - if(its_filter_rule_cfg->type_ == "negative") { - its_filter_type = tc::filter_type_e::NEGATIVE; - } else { - its_filter_type = tc::filter_type_e::POSITIVE; - } - - tc::trace_connector::filter_rule_map_t its_filter_rule_map; - its_filter_rule_map[tc::filter_criteria_e::SERVICES] = its_filter_rule_cfg->services_; - its_filter_rule_map[tc::filter_criteria_e::METHODS] = its_filter_rule_cfg->methods_; - its_filter_rule_map[tc::filter_criteria_e::CLIENTS] = its_filter_rule_cfg->clients_; - - its_filter_rule = std::make_pair(its_filter_type, its_filter_rule_map); - - its_trace_connector->add_filter_rule(it->get()->channel_, its_filter_rule); - } - - bool enable_tracing = its_trace_cfg->is_enabled_; - if (enable_tracing) - its_trace_connector->init(); - its_trace_connector->set_enabled(enable_tracing); - - bool enable_sd_tracing = its_trace_cfg->is_sd_enabled_; - its_trace_connector->set_sd_enabled(enable_sd_tracing); + std::shared_ptr<trace::connector_impl> its_connector + = trace::connector_impl::get(); + std::shared_ptr<cfg::trace> its_trace_configuration + = its_configuration->get_trace(); + its_connector->configure(its_trace_configuration); #endif VSOMEIP_INFO << "Application(" << (name_ != "" ? name_ : "unnamed") - << ", " << std::hex << client_ << ") is initialized (" + << ", " << std::hex << std::setw(4) << std::setfill('0') << client_ + << ") is initialized (" << std::dec << max_dispatchers_ << ", " << std::dec << max_dispatch_time_ << ")."; @@ -266,7 +273,7 @@ bool application_impl::init() { signals_.add(SIGTERM); // Register signal handler - std::function<void(boost::system::error_code const &, int)> its_signal_handler = + auto its_signal_handler = [this] (boost::system::error_code const &_error, int _signal) { if (!_error) { switch (_signal) { @@ -284,19 +291,24 @@ bool application_impl::init() { } #endif - auto its_plugins = configuration_->get_plugins(name_); - auto its_app_plugin_info = its_plugins.find(plugin_type_e::APPLICATION_PLUGIN); - if (its_app_plugin_info != its_plugins.end()) { - for (auto its_library : its_app_plugin_info->second) { - auto its_application_plugin = plugin_manager::get()->get_plugin( - plugin_type_e::APPLICATION_PLUGIN, its_library); - if (its_application_plugin) { - VSOMEIP_INFO << "Client 0x" << std::hex << get_client() - << " Loading plug-in library: " << its_library << " succeeded!"; - std::dynamic_pointer_cast<application_plugin>(its_application_plugin)-> - on_application_state_change(name_, application_plugin_state_e::STATE_INITIALIZED); + if (configuration_) { + auto its_plugins = configuration_->get_plugins(name_); + auto its_app_plugin_info = its_plugins.find(plugin_type_e::APPLICATION_PLUGIN); + if (its_app_plugin_info != its_plugins.end()) { + for (auto its_library : its_app_plugin_info->second) { + auto its_application_plugin = plugin_manager::get()->get_plugin( + plugin_type_e::APPLICATION_PLUGIN, its_library); + if (its_application_plugin) { + VSOMEIP_INFO << "Client 0x" << std::hex << get_client() + << " Loading plug-in library: " << its_library << " succeeded!"; + std::dynamic_pointer_cast<application_plugin>(its_application_plugin)-> + on_application_state_change(name_, application_plugin_state_e::STATE_INITIALIZED); + } } } + } else { + std::cerr << "Configuration module could not be loaded!" << std::endl; + std::exit(EXIT_FAILURE); } return is_initialized_; @@ -313,6 +325,7 @@ void application_impl::start() { } #endif const size_t io_thread_count = configuration_->get_io_thread_count(name_); + const int io_thread_nice_level = configuration_->get_io_thread_nice_level(name_); { std::lock_guard<std::mutex> its_lock(start_stop_mutex_); if (io_.stopped()) { @@ -336,8 +349,13 @@ void application_impl::start() { } stopped_ = false; stopped_called_ = false; - VSOMEIP_INFO << "Starting vsomeip application \"" << name_ << "\" using " - << std::dec << io_thread_count << " threads"; + VSOMEIP_INFO << "Starting vsomeip application \"" << name_ << "\" (" + << std::hex << std::setw(4) << std::setfill('0') << client_ + << ") using " << std::dec << io_thread_count << " threads" +#ifndef _WIN32 + << " I/O nice " << io_thread_nice_level +#endif + ; start_caller_id_ = std::this_thread::get_id(); { @@ -351,14 +369,14 @@ void application_impl::start() { if (stop_thread_.joinable()) { stop_thread_.join(); } - stop_thread_= std::thread(&application_impl::shutdown, this); + stop_thread_= std::thread(&application_impl::shutdown, shared_from_this()); if (routing_) routing_->start(); for (size_t i = 0; i < io_thread_count - 1; i++) { std::shared_ptr<std::thread> its_thread - = std::make_shared<std::thread>([this, i] { + = std::make_shared<std::thread>([this, i, io_thread_nice_level] { VSOMEIP_INFO << "io thread id from application: " << std::hex << std::setw(4) << std::setfill('0') << client_ << " (" << name_ << ") is: " << std::hex @@ -375,13 +393,15 @@ void application_impl::start() { << std::setfill('0') << i+1; pthread_setname_np(pthread_self(),s.str().c_str()); } + if ((VSOMEIP_IO_THREAD_NICE_LEVEL != io_thread_nice_level) && (io_thread_nice_level != nice(io_thread_nice_level))) { + VSOMEIP_WARNING << "nice(" << io_thread_nice_level << ") failed " << errno << " for " << std::this_thread::get_id(); + } #endif try { io_.run(); #ifndef _WIN32 } catch (const boost::log::v2_mt_posix::system_error &e) { - std::cerr << "catched boost::log system_error in I/O thread" << std::endl << - boost::current_exception_diagnostic_information(); + std::cerr << "catched boost::log system_error in I/O thread" << std::endl; #endif } catch (const std::exception &e) { VSOMEIP_ERROR << "application_impl::start() " @@ -416,24 +436,29 @@ void application_impl::start() { << " TID: " << std::dec << static_cast<int>(syscall(SYS_gettid)) #endif ; +#ifndef _WIN32 + if ((VSOMEIP_IO_THREAD_NICE_LEVEL != io_thread_nice_level) && (io_thread_nice_level != nice(io_thread_nice_level))) { + VSOMEIP_WARNING << "nice(" << io_thread_nice_level << ") failed " << errno << " for " << std::this_thread::get_id(); + } +#endif try { io_.run(); + + if (stop_thread_.joinable()) { + stop_thread_.join(); + } + + utility::release_client_id(client_); + utility::auto_configuration_exit(client_, configuration_); + #ifndef _WIN32 } catch (const boost::log::v2_mt_posix::system_error &e) { - std::cerr << "catched boost::log system_error in I/O thread" << std::endl << - boost::current_exception_diagnostic_information(); + std::cerr << "catched boost::log system_error in I/O thread" << std::endl; #endif } catch (const std::exception &e) { VSOMEIP_ERROR << "application_impl::start() catched exception: " << e.what(); } - if (stop_thread_.joinable()) { - stop_thread_.join(); - } - - utility::release_client_id(client_); - utility::auto_configuration_exit(client_, configuration_); - { std::lock_guard<std::mutex> its_lock_start_stop(block_stop_mutex_); block_stopping_ = true; @@ -460,7 +485,12 @@ void application_impl::start() { void application_impl::stop() { #ifndef _WIN32 // Gives serious problems under Windows. - VSOMEIP_INFO << "Stopping vsomeip application \"" << name_ << "\"."; + try { + VSOMEIP_INFO << "Stopping vsomeip application \"" << name_ << "\" (" + << std::hex << std::setw(4) << std::setfill('0') << client_ << ")."; + } catch (const boost::log::v2_mt_posix::system_error &e) { + std::cerr << "catched boost::log system_error application_impl::stop" << std::endl; + } #endif bool block = true; { @@ -593,9 +623,7 @@ bool application_impl::is_available_unlocked( bool is_available(false); - const std::function<void(const std::map<instance_t, - std::map<major_version_t, minor_version_t>>::const_iterator&)> - check_major_minor = [&](const std::map<instance_t, + auto check_major_minor = [&](const std::map<instance_t, std::map<major_version_t, minor_version_t >>::const_iterator &_found_instance) { auto found_major = _found_instance->second.find(_major); @@ -839,7 +867,7 @@ void application_impl::notify_one(service_t _service, instance_t _instance, client_t _client, bool _force) const { if (routing_) { routing_->notify_one(_service, _instance, _event, _payload, _client, - _force, true); + _force, true, false); } } @@ -848,7 +876,7 @@ void application_impl::notify_one(service_t _service, instance_t _instance, client_t _client, bool _force, bool _flush) const { if (routing_) { routing_->notify_one(_service, _instance, _event, _payload, _client, - _force, _flush); + _force, _flush, false); } } @@ -1379,7 +1407,7 @@ void application_impl::on_availability(service_t _service, instance_t _instance, } } - const std::function<void(const availability_major_minor_t&)> find_matching_handler = + auto find_matching_handler = [&](const availability_major_minor_t& _av_ma_mi_it) { auto found_major = _av_ma_mi_it.find(_major); if (found_major != _av_ma_mi_it.end()) { @@ -1650,8 +1678,8 @@ void application_impl::dispatch() { << " TID: " << std::dec << static_cast<int>(syscall(SYS_gettid)) #endif ; + std::unique_lock<std::mutex> its_lock(handlers_mutex_); while (is_active_dispatcher(its_id)) { - std::unique_lock<std::mutex> its_lock(handlers_mutex_); if (is_dispatching_ && handlers_.empty()) { dispatcher_condition_.wait(its_lock); // Maybe woken up from main dispatcher @@ -1680,7 +1708,6 @@ void application_impl::dispatch() { } } } - std::lock_guard<std::mutex> its_lock(handlers_mutex_); if (is_dispatching_) { std::lock_guard<std::mutex> its_lock(dispatcher_mutex_); elapsed_dispatchers_.insert(its_id); @@ -1768,29 +1795,34 @@ void application_impl::invoke_handler(std::shared_ptr<sync_handler> &_handler) { its_dispatcher_timer.async_wait([this, its_id, its_sync_handler](const boost::system::error_code &_error) { if (!_error) { print_blocking_call(its_sync_handler); - bool active_dispatcher_available(false); - if (is_dispatching_) { - std::lock_guard<std::mutex> its_lock(dispatcher_mutex_); - active_dispatcher_available = has_active_dispatcher(); - } - if (active_dispatcher_available) { + if (has_active_dispatcher()) { std::lock_guard<std::mutex> its_lock(handlers_mutex_); dispatcher_condition_.notify_all(); - } else if (is_dispatching_) { + } else { // If possible, create a new dispatcher thread to unblock. // If this is _not_ possible, dispatching is blocked until // at least one of the active handler calls returns. - std::lock_guard<std::mutex> its_lock(dispatcher_mutex_); - if (dispatchers_.size() < max_dispatchers_ && is_dispatching_) { - auto its_dispatcher = std::make_shared<std::thread>( - std::bind(&application_impl::dispatch, shared_from_this())); - dispatchers_[its_dispatcher->get_id()] = its_dispatcher; - } else { - VSOMEIP_ERROR << "Maximum number of dispatchers exceeded."; + while (is_dispatching_) { + if (dispatcher_mutex_.try_lock()) { + if (dispatchers_.size() < max_dispatchers_) { + if (is_dispatching_) { + auto its_dispatcher = std::make_shared<std::thread>( + std::bind(&application_impl::dispatch, shared_from_this())); + dispatchers_[its_dispatcher->get_id()] = its_dispatcher; + } else { + VSOMEIP_INFO << "Won't start new dispatcher " + "thread as Client=" << std::hex + << get_client() << " is shutting down"; + } + } else { + VSOMEIP_ERROR << "Maximum number of dispatchers exceeded."; + } + dispatcher_mutex_.unlock(); + break; + } else { + std::this_thread::yield(); + } } - } else { - VSOMEIP_INFO << "Won't start new dispatcher thread as Client=" - << std::hex << get_client() << " is shutting down"; } } }); @@ -1840,28 +1872,40 @@ void application_impl::invoke_handler(std::shared_ptr<sync_handler> &_handler) { } bool application_impl::has_active_dispatcher() { - for (const auto &d : dispatchers_) { - if (running_dispatchers_.find(d.first) == running_dispatchers_.end() && - elapsed_dispatchers_.find(d.first) == elapsed_dispatchers_.end()) { - return true; + while (is_dispatching_) { + if (dispatcher_mutex_.try_lock()) { + for (const auto &d : dispatchers_) { + if (running_dispatchers_.find(d.first) == running_dispatchers_.end() && + elapsed_dispatchers_.find(d.first) == elapsed_dispatchers_.end()) { + dispatcher_mutex_.unlock(); + return true; + } + } + dispatcher_mutex_.unlock(); + return false; } + std::this_thread::yield(); } return false; } bool application_impl::is_active_dispatcher(const std::thread::id &_id) { - if (!is_dispatching_) { - return is_dispatching_; - } - std::lock_guard<std::mutex> its_lock(dispatcher_mutex_); - for (const auto &d : dispatchers_) { - if (d.first != _id && - running_dispatchers_.find(d.first) == running_dispatchers_.end() && - elapsed_dispatchers_.find(d.first) == elapsed_dispatchers_.end()) { - return false; + while (is_dispatching_) { + if (dispatcher_mutex_.try_lock()) { + for (const auto &d : dispatchers_) { + if (d.first != _id && + running_dispatchers_.find(d.first) == running_dispatchers_.end() && + elapsed_dispatchers_.find(d.first) == elapsed_dispatchers_.end()) { + dispatcher_mutex_.unlock(); + return false; + } + } + dispatcher_mutex_.unlock(); + return true; } + std::this_thread::yield(); } - return true; + return false; } void application_impl::remove_elapsed_dispatchers() { @@ -1978,14 +2022,15 @@ void application_impl::shutdown() { { std::lock_guard<std::mutex> its_lock_start_stop(start_stop_mutex_); for (auto t : io_threads_) { - t->join(); + if (t->joinable()) { + t->join(); + } } io_threads_.clear(); } #ifndef _WIN32 } catch (const boost::log::v2_mt_posix::system_error &e) { - std::cerr << "catched boost::log system_error in stop thread" << std::endl << - boost::current_exception_diagnostic_information(); + std::cerr << "catched boost::log system_error in stop thread" << std::endl; #endif } catch (const std::exception &e) { VSOMEIP_ERROR << "application_impl::shutdown() catched exception: " << e.what(); @@ -2361,4 +2406,129 @@ void application_impl::register_async_subscription_handler(service_t _service, register_message_handler(_service, _instance, ANY_METHOD - 1, handler); } +void application_impl::register_offer_acceptance_handler( + offer_acceptance_handler_t _handler) { + if (is_routing() && routing_) { + const auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + rm_impl->register_offer_acceptance_handler(_handler); + } +} + +void application_impl::register_reboot_notification_handler( + reboot_notification_handler_t _handler) { + if (is_routing() && routing_) { + const auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + rm_impl->register_reboot_notification_handler(_handler); + } +} + +void application_impl::set_offer_acceptance_required( + ip_address_t _address, const std::string _path, bool _enable) { + if (is_routing()) { + const boost::asio::ip::address its_address = _address.is_v4_ ? + static_cast<boost::asio::ip::address>(boost::asio::ip::address_v4(_address.address_.v4_)) : + static_cast<boost::asio::ip::address>(boost::asio::ip::address_v6(_address.address_.v6_)); + configuration_->set_offer_acceptance_required(its_address, _path, _enable); + if (_enable && routing_) { + const auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + rm_impl->offer_acceptance_enabled(its_address); + } + } +} + +vsomeip::application::offer_acceptance_map_type_t +application_impl::get_offer_acceptance_required() { + offer_acceptance_map_type_t its_ret; + if (is_routing()) { + for (const auto& e : configuration_->get_offer_acceptance_required()) { + ip_address_t its_address; + its_address.is_v4_ = e.first.is_v4(); + if (its_address.is_v4_) { + its_address.address_.v4_ = e.first.to_v4().to_bytes(); + } else { + its_address.address_.v6_ = e.first.to_v6().to_bytes(); + } + its_ret[its_address] = e.second; + } + } + return its_ret; +} + +void application_impl::register_routing_ready_handler( + routing_ready_handler_t _handler) { + if (is_routing() && routing_) { + const auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + rm_impl->register_routing_ready_handler(_handler); + } +} + +void application_impl::register_routing_state_handler( + routing_state_handler_t _handler) { + if (is_routing() && routing_) { + const auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + rm_impl->register_routing_state_handler(_handler); + } +} + +bool application_impl::update_service_configuration(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled, + bool _offer) { + bool ret = false; + if (!is_routing_manager_host_) { + VSOMEIP_ERROR << __func__ << " is only intended to be called by " + "application acting as routing manager host"; + } else if (!routing_) { + VSOMEIP_ERROR << __func__ << " routing is zero"; + } else { + auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + if (rm_impl) { + if (_offer) { + ret = rm_impl->offer_service_remotely(_service, _instance, + _port, _reliable, _magic_cookies_enabled); + } else { + ret = rm_impl->stop_offer_service_remotely(_service, _instance, + _port, _reliable, _magic_cookies_enabled); + } + } + } + return ret; +} + +void application_impl::update_security_policy_configuration(uint32_t _uid, + uint32_t _gid, + ::std::shared_ptr<policy> _policy, + std::shared_ptr<payload> _payload, + security_update_handler_t _handler) { + if (!is_routing()) { + VSOMEIP_ERROR << __func__ << " is only intended to be called by " + "application acting as routing manager host"; + } else if (!routing_) { + VSOMEIP_ERROR << __func__ << " routing is zero"; + } else { + auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + if (rm_impl) { + rm_impl->update_security_policy_configuration(_uid, _gid, _policy, _payload, _handler); + } + } +} + +void application_impl::remove_security_policy_configuration(uint32_t _uid, + uint32_t _gid, + security_update_handler_t _handler) { + if (!is_routing()) { + VSOMEIP_ERROR << __func__ << " is only intended to be called by " + "application acting as routing manager host"; + } else if (!routing_) { + VSOMEIP_ERROR << __func__ << " routing is zero"; + } else { + auto rm_impl = std::dynamic_pointer_cast<routing_manager_impl>(routing_); + if (rm_impl) { + rm_impl->remove_security_policy_configuration(_uid, _gid, _handler); + } + } +} + } // namespace vsomeip diff --git a/implementation/service_discovery/include/entry_impl.hpp b/implementation/service_discovery/include/entry_impl.hpp index db12a1e..b41a94c 100755 --- a/implementation/service_discovery/include/entry_impl.hpp +++ b/implementation/service_discovery/include/entry_impl.hpp @@ -40,7 +40,7 @@ public: void set_instance(instance_t _instance);
major_version_t get_major_version() const;
- void set_major_version(major_version_t _version);
+ void set_major_version(major_version_t _major_version);
ttl_t get_ttl() const;
void set_ttl(ttl_t _ttl);
@@ -72,7 +72,7 @@ protected: std::uint8_t index2_;
entry_impl();
- entry_impl(const entry_impl &entry_);
+ entry_impl(const entry_impl &_entry);
};
} // namespace sd
diff --git a/implementation/service_discovery/include/ip_option_impl.hpp b/implementation/service_discovery/include/ip_option_impl.hpp index 1345835..a2a7660 100644 --- a/implementation/service_discovery/include/ip_option_impl.hpp +++ b/implementation/service_discovery/include/ip_option_impl.hpp @@ -17,7 +17,7 @@ class ip_option_impl: public option_impl { public: ip_option_impl(); virtual ~ip_option_impl(); - virtual bool operator ==(const ip_option_impl &_option) const; + virtual bool operator ==(const ip_option_impl &_other) const; uint16_t get_port() const; void set_port(uint16_t _port); diff --git a/implementation/service_discovery/include/message_impl.hpp b/implementation/service_discovery/include/message_impl.hpp index baca328..83d3a64 100755 --- a/implementation/service_discovery/include/message_impl.hpp +++ b/implementation/service_discovery/include/message_impl.hpp @@ -98,7 +98,7 @@ public: void forced_initial_events_add(forced_initial_events_t _entry);
const std::vector<forced_initial_events_t> forced_initial_events_get();
- void set_initial_events_required(bool _initial_events);
+ void set_initial_events_required(bool _initial_events_required);
bool initial_events_required() const;
private:
diff --git a/implementation/service_discovery/include/runtime.hpp b/implementation/service_discovery/include/runtime.hpp index f99fec8..670a7bb 100644 --- a/implementation/service_discovery/include/runtime.hpp +++ b/implementation/service_discovery/include/runtime.hpp @@ -10,6 +10,8 @@ namespace vsomeip {
+class configuration;
+
namespace sd {
class message_impl;
@@ -22,7 +24,8 @@ public: }
virtual std::shared_ptr<service_discovery> create_service_discovery(
- service_discovery_host *_host) const = 0;
+ service_discovery_host *_host,
+ std::shared_ptr<vsomeip::configuration> _configuration) const = 0;
virtual std::shared_ptr<message_impl> create_message() const = 0;
};
diff --git a/implementation/service_discovery/include/runtime_impl.hpp b/implementation/service_discovery/include/runtime_impl.hpp index dcdb7d7..0a9baaf 100644 --- a/implementation/service_discovery/include/runtime_impl.hpp +++ b/implementation/service_discovery/include/runtime_impl.hpp @@ -20,7 +20,8 @@ public: virtual ~runtime_impl(); std::shared_ptr<service_discovery> create_service_discovery( - service_discovery_host *_host) const; + service_discovery_host *_host, + std::shared_ptr<configuration> _configuration) const; std::shared_ptr<message_impl> create_message() const; }; diff --git a/implementation/service_discovery/include/service_discovery.hpp b/implementation/service_discovery/include/service_discovery.hpp index 92d80d3..e1c3dc4 100644 --- a/implementation/service_discovery/include/service_discovery.hpp +++ b/implementation/service_discovery/include/service_discovery.hpp @@ -11,6 +11,7 @@ #include <vsomeip/primitive_types.hpp> #include <vsomeip/enumeration_types.hpp> +#include <vsomeip/handler.hpp> #include "../../routing/include/serviceinfo.hpp" #include "../../endpoints/include/endpoint.hpp" #include "../include/service_discovery_host.hpp" @@ -26,7 +27,6 @@ public: virtual ~service_discovery() { } - virtual std::shared_ptr<configuration> get_configuration() const = 0; virtual boost::asio::io_service & get_io() = 0; virtual void init() = 0; @@ -72,6 +72,11 @@ public: service_t _service, instance_t _instance, eventgroup_t _eventgroup, client_t _client, bool _accepted, const std::shared_ptr<sd_message_identifier_t> &_sd_message_id) = 0; + + virtual void register_offer_acceptance_handler( + vsomeip::offer_acceptance_handler_t _handler) = 0; + virtual void register_reboot_notification_handler( + reboot_notification_handler_t _handler) = 0; }; } // namespace sd diff --git a/implementation/service_discovery/include/service_discovery_host.hpp b/implementation/service_discovery/include/service_discovery_host.hpp index f9e8f29..9c346fd 100644 --- a/implementation/service_discovery/include/service_discovery_host.hpp +++ b/implementation/service_discovery/include/service_discovery_host.hpp @@ -31,7 +31,6 @@ public: } virtual boost::asio::io_service & get_io() = 0; - virtual const std::shared_ptr<configuration> get_configuration() const = 0; virtual std::shared_ptr<endpoint> create_service_discovery_endpoint( const std::string &_address, uint16_t _port, bool _reliable) = 0; @@ -90,7 +89,7 @@ public: virtual bool has_identified(client_t _client, service_t _service, instance_t _instance, bool _reliable) = 0; - virtual std::chrono::steady_clock::time_point expire_subscriptions() = 0; + virtual std::chrono::steady_clock::time_point expire_subscriptions(bool _force) = 0; virtual std::shared_ptr<serviceinfo> get_offered_service( service_t _service, instance_t _instance) const = 0; diff --git a/implementation/service_discovery/include/service_discovery_impl.hpp b/implementation/service_discovery/include/service_discovery_impl.hpp index 995cfbc..77dbae9 100644 --- a/implementation/service_discovery/include/service_discovery_impl.hpp +++ b/implementation/service_discovery/include/service_discovery_impl.hpp @@ -56,10 +56,10 @@ struct subscriber_t { class service_discovery_impl: public service_discovery, public std::enable_shared_from_this<service_discovery_impl> { public: - service_discovery_impl(service_discovery_host *_host); + service_discovery_impl(service_discovery_host *_host, + std::shared_ptr<configuration> _configuration); virtual ~service_discovery_impl(); - std::shared_ptr<configuration> get_configuration() const; boost::asio::io_service & get_io(); void init(); @@ -102,6 +102,10 @@ public: service_t _service, instance_t _instance, eventgroup_t _eventgroup, client_t _client, bool _acknowledged, const std::shared_ptr<sd_message_identifier_t> &_sd_message_id); + + void register_offer_acceptance_handler(offer_acceptance_handler_t _handler); + void register_reboot_notification_handler( + reboot_notification_handler_t _handler); private: std::pair<session_t, bool> get_session(const boost::asio::ip::address &_address); void increment_session(const boost::asio::ip::address &_address); @@ -149,7 +153,9 @@ private: void process_serviceentry(std::shared_ptr<serviceentry_impl> &_entry, const std::vector<std::shared_ptr<option_impl> > &_options, - bool _unicast_flag, std::vector<std::pair<std::uint16_t, std::shared_ptr<message_impl>>>* _resubscribes); + bool _unicast_flag, + std::vector<std::pair<std::uint16_t, std::shared_ptr<message_impl>>>* _resubscribes, + bool _accept_offers); void process_offerservice_serviceentry( service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor, ttl_t _ttl, @@ -210,7 +216,7 @@ private: void stop_subscription_expiration_timer_unlocked(); void expire_subscriptions(const boost::system::error_code &_error); - bool check_ipv4_address(boost::asio::ip::address its_address); + bool check_ipv4_address(const boost::asio::ip::address& its_address) const; bool check_static_header_fields( const std::shared_ptr<const message> &_message) const; @@ -346,6 +352,7 @@ private: private: boost::asio::io_service &io_; service_discovery_host *host_; + std::shared_ptr<configuration> configuration_; boost::asio::ip::address unicast_; uint16_t port_; @@ -443,6 +450,12 @@ private: std::mutex remote_offer_types_mutex_; std::map<std::pair<service_t, instance_t>, remote_offer_type_e> remote_offer_types_; std::map<boost::asio::ip::address, std::set<std::pair<service_t, instance_t>>> remote_offers_by_ip_; + + reboot_notification_handler_t reboot_notification_handler_; + offer_acceptance_handler_t offer_acceptance_handler_; + + std::mutex offer_mutex_; + std::mutex check_ttl_mutex_; }; } // namespace sd diff --git a/implementation/service_discovery/src/configuration_option_impl.cpp b/implementation/service_discovery/src/configuration_option_impl.cpp index e9cf058..e09c7c9 100755 --- a/implementation/service_discovery/src/configuration_option_impl.cpp +++ b/implementation/service_discovery/src/configuration_option_impl.cpp @@ -100,7 +100,7 @@ bool configuration_option_impl::deserialize(vsomeip::deserializer *_from) { is_successful = is_successful && _from->deserialize(l_itemLength);
if (l_itemLength > 0) {
is_successful = is_successful
- && _from->deserialize((uint8_t*) &l_item[0], l_itemLength);
+ && _from->deserialize(l_item, static_cast<std::size_t>(l_itemLength));
if (is_successful) {
size_t l_eqPos = l_item.find('='); //SWS_SD_00292
diff --git a/implementation/service_discovery/src/eventgroupentry_impl.cpp b/implementation/service_discovery/src/eventgroupentry_impl.cpp index f5394be..17fd63b 100755 --- a/implementation/service_discovery/src/eventgroupentry_impl.cpp +++ b/implementation/service_discovery/src/eventgroupentry_impl.cpp @@ -150,37 +150,42 @@ bool eventgroupentry_impl::is_matching_subscribe( // read out ip options of current and _other
std::vector<std::shared_ptr<ip_option_impl>> its_options_current;
std::vector<std::shared_ptr<ip_option_impl>> its_options_other;
+ const std::size_t its_options_size = _options.size();
for (const auto option_run : {0,1}) {
for (const auto option_index : options_[option_run]) {
- switch (_options[option_index]->get_type()) {
- case option_type_e::IP4_ENDPOINT:
- its_options_current.push_back(
- std::static_pointer_cast<ipv4_option_impl>(
- _options[option_index]));
- break;
- case option_type_e::IP6_ENDPOINT:
- its_options_current.push_back(
- std::static_pointer_cast<ipv6_option_impl>(
- _options[option_index]));
- break;
- default:
- break;
+ if (its_options_size > option_index) {
+ switch (_options[option_index]->get_type()) {
+ case option_type_e::IP4_ENDPOINT:
+ its_options_current.push_back(
+ std::static_pointer_cast<ipv4_option_impl>(
+ _options[option_index]));
+ break;
+ case option_type_e::IP6_ENDPOINT:
+ its_options_current.push_back(
+ std::static_pointer_cast<ipv6_option_impl>(
+ _options[option_index]));
+ break;
+ default:
+ break;
+ }
}
}
for (const auto option_index : _other.options_[option_run]) {
- switch (_options[option_index]->get_type()) {
- case option_type_e::IP4_ENDPOINT:
- its_options_other.push_back(
- std::static_pointer_cast<ipv4_option_impl>(
- _options[option_index]));
- break;
- case option_type_e::IP6_ENDPOINT:
- its_options_other.push_back(
- std::static_pointer_cast<ipv6_option_impl>(
- _options[option_index]));
- break;
- default:
- break;
+ if (its_options_size > option_index) {
+ switch (_options[option_index]->get_type()) {
+ case option_type_e::IP4_ENDPOINT:
+ its_options_other.push_back(
+ std::static_pointer_cast<ipv4_option_impl>(
+ _options[option_index]));
+ break;
+ case option_type_e::IP6_ENDPOINT:
+ its_options_other.push_back(
+ std::static_pointer_cast<ipv6_option_impl>(
+ _options[option_index]));
+ break;
+ default:
+ break;
+ }
}
}
}
diff --git a/implementation/service_discovery/src/message_impl.cpp b/implementation/service_discovery/src/message_impl.cpp index 9d5c1ac..e3b54ec 100755 --- a/implementation/service_discovery/src/message_impl.cpp +++ b/implementation/service_discovery/src/message_impl.cpp @@ -196,17 +196,17 @@ bool message_impl::serialize(vsomeip::serializer *_to) const { uint32_t entries_length = uint32_t(entries_.size() * VSOMEIP_SOMEIP_SD_ENTRY_SIZE);
is_successful = is_successful && _to->serialize(entries_length);
- for (auto it = entries_.begin(); it != entries_.end(); ++it)
- is_successful = is_successful && (*it)->serialize(_to);
+ for (const auto& its_entry : entries_)
+ is_successful = is_successful && its_entry && its_entry->serialize(_to);
uint32_t options_length = 0;
- for (auto its_option : options_)
- options_length += its_option->get_length()
- + VSOMEIP_SOMEIP_SD_OPTION_HEADER_SIZE;
+ for (const auto& its_option : options_)
+ options_length += its_option ? its_option->get_length()
+ + VSOMEIP_SOMEIP_SD_OPTION_HEADER_SIZE : 0;
is_successful = is_successful && _to->serialize(options_length);
- for (auto its_option : options_)
- is_successful = is_successful && its_option->serialize(_to);
+ for (const auto& its_option : options_)
+ is_successful = is_successful && its_option && its_option->serialize(_to);
return is_successful;
}
@@ -231,6 +231,14 @@ bool message_impl::deserialize(vsomeip::deserializer *_from) { // backup the current remaining length
uint32_t save_remaining = uint32_t(_from->get_remaining());
+ if (!is_successful) {
+ // couldn't deserialize entries length
+ return is_successful;
+ } else if (entries_length > save_remaining) {
+ // not enough data available to deserialize entries array
+ is_successful = false;
+ return is_successful;
+ }
// set remaining bytes to length of entries array
_from->set_remaining(entries_length);
diff --git a/implementation/service_discovery/src/runtime_impl.cpp b/implementation/service_discovery/src/runtime_impl.cpp index 90743b8..d72d034 100644 --- a/implementation/service_discovery/src/runtime_impl.cpp +++ b/implementation/service_discovery/src/runtime_impl.cpp @@ -25,8 +25,9 @@ runtime_impl::~runtime_impl() { } std::shared_ptr<service_discovery> runtime_impl::create_service_discovery( - service_discovery_host *_host) const { - return std::make_shared < service_discovery_impl > (_host); + service_discovery_host *_host, + std::shared_ptr<configuration> _configuration) const { + return std::make_shared < service_discovery_impl > (_host, _configuration); } std::shared_ptr<message_impl> runtime_impl::create_message() const { diff --git a/implementation/service_discovery/src/service_discovery_impl.cpp b/implementation/service_discovery/src/service_discovery_impl.cpp index 1f93559..de5f190 100644 --- a/implementation/service_discovery/src/service_discovery_impl.cpp +++ b/implementation/service_discovery/src/service_discovery_impl.cpp @@ -34,21 +34,22 @@ #include "../../routing/include/eventgroupinfo.hpp" #include "../../routing/include/serviceinfo.hpp" #include "../../plugin/include/plugin_manager.hpp" +#include "../../utility/include/byteorder.hpp" namespace vsomeip { namespace sd { -service_discovery_impl::service_discovery_impl(service_discovery_host *_host) +service_discovery_impl::service_discovery_impl(service_discovery_host *_host, + std::shared_ptr<configuration> _configuration) : io_(_host->get_io()), host_(_host), + configuration_(_configuration), port_(VSOMEIP_SD_DEFAULT_PORT), reliable_(false), - serializer_( - std::make_shared<serializer>( - host_->get_configuration()->get_buffer_shrink_threshold())), - deserializer_( - std::make_shared<deserializer>( - host_->get_configuration()->get_buffer_shrink_threshold())), + serializer_(std::make_shared<serializer>( + configuration_->get_buffer_shrink_threshold())), + deserializer_(std::make_shared<deserializer>( + configuration_->get_buffer_shrink_threshold())), ttl_timer_(_host->get_io()), ttl_timer_runtime_(VSOMEIP_SD_DEFAULT_CYCLIC_OFFER_DELAY / 2), ttl_(VSOMEIP_SD_DEFAULT_TTL), @@ -75,10 +76,6 @@ service_discovery_impl::service_discovery_impl(service_discovery_host *_host) service_discovery_impl::~service_discovery_impl() { } -std::shared_ptr<configuration> service_discovery_impl::get_configuration() const { - return host_->get_configuration(); -} - boost::asio::io_service & service_discovery_impl::get_io() { return io_; } @@ -86,62 +83,55 @@ boost::asio::io_service & service_discovery_impl::get_io() { void service_discovery_impl::init() { runtime_ = std::dynamic_pointer_cast<sd::runtime>(plugin_manager::get()->get_plugin(plugin_type_e::SD_RUNTIME_PLUGIN, VSOMEIP_SD_LIBRARY)); - std::shared_ptr < configuration > its_configuration = - host_->get_configuration(); - if (its_configuration) { - unicast_ = its_configuration->get_unicast_address(); - sd_multicast_ = its_configuration->get_sd_multicast(); - boost::system::error_code ec; - sd_multicast_address_ = boost::asio::ip::address::from_string(sd_multicast_, ec); - - port_ = its_configuration->get_sd_port(); - reliable_ = (its_configuration->get_sd_protocol() - == "tcp"); - max_message_size_ = (reliable_ ? VSOMEIP_MAX_TCP_SD_PAYLOAD : - VSOMEIP_MAX_UDP_SD_PAYLOAD); - - ttl_ = its_configuration->get_sd_ttl(); - - // generate random initial delay based on initial delay min and max - std::int32_t initial_delay_min = - its_configuration->get_sd_initial_delay_min(); - if (initial_delay_min < 0) { - initial_delay_min = VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MIN; - } - std::int32_t initial_delay_max = - its_configuration->get_sd_initial_delay_max(); - if (initial_delay_max < 0) { - initial_delay_max = VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MAX; - } - if (initial_delay_min > initial_delay_max) { - const std::uint32_t tmp(initial_delay_min); - initial_delay_min = initial_delay_max; - initial_delay_max = tmp; - } - - std::random_device r; - std::mt19937 e(r()); - std::uniform_int_distribution<std::uint32_t> distribution( - initial_delay_min, initial_delay_max); - initial_delay_ = std::chrono::milliseconds(distribution(e)); - - - repetitions_base_delay_ = std::chrono::milliseconds( - its_configuration->get_sd_repetitions_base_delay()); - repetitions_max_ = its_configuration->get_sd_repetitions_max(); - cyclic_offer_delay_ = std::chrono::milliseconds( - its_configuration->get_sd_cyclic_offer_delay()); - offer_debounce_time_ = std::chrono::milliseconds( - its_configuration->get_sd_offer_debounce_time()); - ttl_timer_runtime_ = cyclic_offer_delay_ / 2; - - ttl_factor_offers_ = its_configuration->get_ttl_factor_offers(); - ttl_factor_subscriptions_ = its_configuration->get_ttl_factor_subscribes(); - last_msg_received_timer_timeout_ = cyclic_offer_delay_ - + (cyclic_offer_delay_ / 10); - } else { - VSOMEIP_ERROR << "SD: no configuration found!"; + unicast_ = configuration_->get_unicast_address(); + sd_multicast_ = configuration_->get_sd_multicast(); + boost::system::error_code ec; + sd_multicast_address_ = boost::asio::ip::address::from_string(sd_multicast_, ec); + + port_ = configuration_->get_sd_port(); + reliable_ = (configuration_->get_sd_protocol() == "tcp"); + max_message_size_ = (reliable_ ? VSOMEIP_MAX_TCP_SD_PAYLOAD : + VSOMEIP_MAX_UDP_SD_PAYLOAD); + + ttl_ = configuration_->get_sd_ttl(); + + // generate random initial delay based on initial delay min and max + std::int32_t initial_delay_min = + configuration_->get_sd_initial_delay_min(); + if (initial_delay_min < 0) { + initial_delay_min = VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MIN; + } + std::int32_t initial_delay_max = + configuration_->get_sd_initial_delay_max(); + if (initial_delay_max < 0) { + initial_delay_max = VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MAX; } + if (initial_delay_min > initial_delay_max) { + const std::uint32_t tmp(initial_delay_min); + initial_delay_min = initial_delay_max; + initial_delay_max = tmp; + } + + std::random_device r; + std::mt19937 e(r()); + std::uniform_int_distribution<std::uint32_t> distribution( + initial_delay_min, initial_delay_max); + initial_delay_ = std::chrono::milliseconds(distribution(e)); + + + repetitions_base_delay_ = std::chrono::milliseconds( + configuration_->get_sd_repetitions_base_delay()); + repetitions_max_ = configuration_->get_sd_repetitions_max(); + cyclic_offer_delay_ = std::chrono::milliseconds( + configuration_->get_sd_cyclic_offer_delay()); + offer_debounce_time_ = std::chrono::milliseconds( + configuration_->get_sd_offer_debounce_time()); + ttl_timer_runtime_ = cyclic_offer_delay_ / 2; + + ttl_factor_offers_ = configuration_->get_ttl_factor_offers(); + ttl_factor_subscriptions_ = configuration_->get_ttl_factor_subscribes(); + last_msg_received_timer_timeout_ = cyclic_offer_delay_ + + (cyclic_offer_delay_ / 10); } void service_discovery_impl::start() { @@ -300,7 +290,7 @@ void service_discovery_impl::subscribe(service_t _service, instance_t _instance, if (its_offer_type == remote_offer_type_e::UNRELIABLE && !its_subscription->get_endpoint(true) && its_subscription->get_endpoint(false)) { - if (its_subscription->get_endpoint(false)->is_connected()) { + if (its_subscription->get_endpoint(false)->is_established()) { insert_subscription(its_message, _service, _instance, _eventgroup, @@ -311,7 +301,7 @@ void service_discovery_impl::subscribe(service_t _service, instance_t _instance, } else if (its_offer_type == remote_offer_type_e::RELIABLE && its_subscription->get_endpoint(true) && !its_subscription->get_endpoint(false)) { - if (its_subscription->get_endpoint(true)->is_connected()) { + if (its_subscription->get_endpoint(true)->is_established()) { insert_subscription(its_message, _service, _instance, _eventgroup, @@ -322,17 +312,17 @@ void service_discovery_impl::subscribe(service_t _service, instance_t _instance, } else if (its_offer_type == remote_offer_type_e::RELIABLE_UNRELIABLE && its_subscription->get_endpoint(true) && its_subscription->get_endpoint(false)) { - if (its_subscription->get_endpoint(true)->is_connected() && - its_subscription->get_endpoint(false)->is_connected()) { + if (its_subscription->get_endpoint(true)->is_established() && + its_subscription->get_endpoint(false)->is_established()) { insert_subscription(its_message, _service, _instance, _eventgroup, its_subscription, its_offer_type); } else { - if (!its_subscription->get_endpoint(true)->is_connected()) { + if (!its_subscription->get_endpoint(true)->is_established()) { its_subscription->set_tcp_connection_established(false); } - if (!its_subscription->get_endpoint(false)->is_connected()) { + if (!its_subscription->get_endpoint(false)->is_established()) { its_subscription->set_udp_connection_established(false); } } @@ -446,13 +436,7 @@ void service_discovery_impl::unsubscribe_all(service_t _service, instance_t _ins if (found_service != subscribed_.end()) { auto found_instance = found_service->second.find(_instance); if (found_instance != found_service->second.end()) { - for (auto &its_eventgroup : found_instance->second) { - for (auto its_client : its_eventgroup.second) { - its_client.second->set_acknowledged(true); - its_client.second->set_endpoint(nullptr, true); - its_client.second->set_endpoint(nullptr, false); - } - } + found_instance->second.clear(); } } } @@ -813,11 +797,15 @@ void service_discovery_impl::insert_offer_entries( for (const auto its_service : _services) { for (const auto its_instance : its_service.second) { if ((!is_suspended_) - && ((!is_diagnosis_) || (is_diagnosis_ && !host_->get_configuration()->is_someip(its_service.first, its_instance.first)))) { + && ((!is_diagnosis_) + || (is_diagnosis_ + && !configuration_->is_someip(its_service.first, + its_instance.first)))) { // Only insert services with configured endpoint(s) if ((_ignore_phase || its_instance.second->is_in_mainphase()) && (its_instance.second->get_endpoint(false) - || its_instance.second->get_endpoint(true))) { + || its_instance.second->get_endpoint(true)) + && its_instance.second->get_ttl() > 0) { if (i >= _start) { if (!insert_offer_service(_message, its_service.first, its_instance.first, its_instance.second, its_size)) { @@ -935,8 +923,7 @@ bool service_discovery_impl::insert_nack_subscription_on_resubscribe(std::shared // Two entries: Stop subscribe & subscribe within one SD-Message // One option: Both entries reference it - const std::function<std::shared_ptr<eventgroupentry_impl>(ttl_t)> insert_entry - = [&](ttl_t _ttl) { + auto insert_entry = [&](ttl_t _ttl) { std::shared_ptr<eventgroupentry_impl> its_entry = _message->create_eventgroup_entry(); // SUBSCRIBE_EVENTGROUP and STOP_SUBSCRIBE_EVENTGROUP are identical @@ -955,7 +942,7 @@ bool service_discovery_impl::insert_nack_subscription_on_resubscribe(std::shared if (_offer_type == remote_offer_type_e::UNRELIABLE && !its_reliable_endpoint && its_unreliable_endpoint) { - if (its_unreliable_endpoint->is_connected()) { + if (its_unreliable_endpoint->is_established()) { const std::uint16_t its_port = its_unreliable_endpoint->get_local_port(); if (its_port) { std::shared_ptr<eventgroupentry_impl> its_stop_entry = insert_entry(0); @@ -975,7 +962,7 @@ bool service_discovery_impl::insert_nack_subscription_on_resubscribe(std::shared } } else if (_offer_type == remote_offer_type_e::RELIABLE && its_reliable_endpoint && !its_unreliable_endpoint) { - if (its_reliable_endpoint->is_connected()) { + if (its_reliable_endpoint->is_established()) { const std::uint16_t its_port = its_reliable_endpoint->get_local_port(); if (its_port) { std::shared_ptr<eventgroupentry_impl> its_stop_entry = insert_entry(0); @@ -995,8 +982,8 @@ bool service_discovery_impl::insert_nack_subscription_on_resubscribe(std::shared } } else if (_offer_type == remote_offer_type_e::RELIABLE_UNRELIABLE && its_reliable_endpoint && its_unreliable_endpoint) { - if (its_reliable_endpoint->is_connected() && - its_unreliable_endpoint->is_connected()) { + if (its_reliable_endpoint->is_established() && + its_unreliable_endpoint->is_established()) { const std::uint16_t its_reliable_port = its_reliable_endpoint->get_local_port(); const std::uint16_t its_unreliable_port = its_unreliable_endpoint->get_local_port(); if (its_reliable_port && its_unreliable_port) { @@ -1021,10 +1008,10 @@ bool service_discovery_impl::insert_nack_subscription_on_resubscribe(std::shared << std::hex << std::setw(4) << std::setfill('0') << _eventgroup << "]"; } } else { - if (!its_reliable_endpoint->is_connected()) { + if (!its_reliable_endpoint->is_established()) { _subscription->set_tcp_connection_established(false); } - if (!its_unreliable_endpoint->is_connected()) { + if (!its_unreliable_endpoint->is_established()) { _subscription->set_udp_connection_established(false); } } @@ -1127,6 +1114,7 @@ bool service_discovery_impl::send(bool _is_announcing) { its_message = its_runtime->create_message(); its_messages.push_back(its_message); + std::lock_guard<std::mutex> its_lock(offer_mutex_); services_t its_offers = host_->get_offered_services(); fill_message_with_offer_entries(its_runtime, its_message, its_messages, its_offers, false); @@ -1149,6 +1137,7 @@ void service_discovery_impl::on_message(const byte_t *_data, length_t _length, msg << std::hex << std::setw(2) << std::setfill('0') << (int)_data[i] << " "; VSOMEIP_INFO << msg.str(); #endif + std::lock_guard<std::mutex> its_lock(check_ttl_mutex_); std::lock_guard<std::mutex> its_session_lock(sessions_received_mutex_); if(is_suspended_) { @@ -1186,8 +1175,18 @@ void service_discovery_impl::on_message(const byte_t *_data, length_t _length, remove_remote_offer_type_by_ip(_sender); host_->expire_subscriptions(_sender); host_->expire_services(_sender); + if (reboot_notification_handler_) { + ip_address_t ip; + if (_sender.is_v4()) { + ip.address_.v4_ = _sender.to_v4().to_bytes(); + ip.is_v4_ = true; + } else { + ip.address_.v6_ = _sender.to_v6().to_bytes(); + ip.is_v4_ = false; + } + reboot_notification_handler_(ip); + } } - std::vector < std::shared_ptr<option_impl> > its_options = its_message->get_options(); @@ -1199,7 +1198,7 @@ void service_discovery_impl::on_message(const byte_t *_data, length_t _length, std::shared_ptr < message_impl > its_message_response = its_runtime->create_message(); - const std::uint8_t its_required_acks = + std::uint8_t its_required_acks = its_message->get_number_required_acks(); its_message_response->set_number_required_acks(its_required_acks); std::shared_ptr<sd_message_identifier_t> its_message_id = @@ -1215,27 +1214,66 @@ void service_discovery_impl::on_message(const byte_t *_data, length_t _length, const message_impl::entries_t::const_iterator its_end = its_entries.end(); bool is_stop_subscribe_subscribe(false); + bool offer_acceptance_queried(false); + bool accept_offers(false); + bool expired_services(false); + for (auto iter = its_entries.begin(); iter != its_end; iter++) { + if (!offer_acceptance_queried) { + if (offer_acceptance_handler_) { + ip_address_t ip; + if (configuration_->offer_acceptance_required(_sender)) { + if (_sender.is_v4()) { + ip.address_.v4_ = _sender.to_v4().to_bytes(); + ip.is_v4_ = true; + } else { + ip.address_.v6_ = _sender.to_v6().to_bytes(); + ip.is_v4_ = false; + } + accept_offers = offer_acceptance_handler_(ip); + if (!accept_offers && !expired_services) { + + VSOMEIP_INFO << "service_discovery_impl::on_message: Do not accept offer / subscribe from: " + << std::hex << std::setw(4) << std::setfill('0') << _sender.to_string(); + + remove_remote_offer_type_by_ip(_sender); + host_->expire_subscriptions(_sender); + host_->expire_services(_sender); + expired_services = true; + } + } else { + accept_offers = true; + } + offer_acceptance_queried = true; + } else { + offer_acceptance_queried = true; + accept_offers = true; + } + } if ((*iter)->is_service_entry()) { std::shared_ptr < serviceentry_impl > its_service_entry = std::dynamic_pointer_cast < serviceentry_impl > (*iter); bool its_unicast_flag = its_message->get_unicast_flag(); process_serviceentry(its_service_entry, its_options, - its_unicast_flag, &its_resubscribes); + its_unicast_flag, &its_resubscribes, accept_offers); } else { - std::shared_ptr < eventgroupentry_impl > its_eventgroup_entry = - std::dynamic_pointer_cast < eventgroupentry_impl - > (*iter); - bool force_initial_events(false); - if (is_stop_subscribe_subscribe) { - force_initial_events = true; + if (accept_offers) { + std::shared_ptr < eventgroupentry_impl > its_eventgroup_entry = + std::dynamic_pointer_cast < eventgroupentry_impl + > (*iter); + bool force_initial_events(false); + if (is_stop_subscribe_subscribe) { + force_initial_events = true; + } + is_stop_subscribe_subscribe = check_stop_subscribe_subscribe( + iter, its_end, its_message->get_options()); + process_eventgroupentry(its_eventgroup_entry, its_options, + its_message_response, _destination, + its_message_id, is_stop_subscribe_subscribe, force_initial_events); + } else { + its_required_acks = 0; } - is_stop_subscribe_subscribe = check_stop_subscribe_subscribe( - iter, its_end, its_message->get_options()); - process_eventgroupentry(its_eventgroup_entry, its_options, - its_message_response, _destination, - its_message_id, is_stop_subscribe_subscribe, force_initial_events); } } @@ -1283,7 +1321,8 @@ void service_discovery_impl::process_serviceentry( std::shared_ptr<serviceentry_impl> &_entry, const std::vector<std::shared_ptr<option_impl> > &_options, bool _unicast_flag, - std::vector<std::pair<std::uint16_t, std::shared_ptr<message_impl>>>* _resubscribes) { + std::vector<std::pair<std::uint16_t, std::shared_ptr<message_impl>>>* _resubscribes, + bool _accept_offers) { // Read service info from entry entry_type_e its_type = _entry->get_type(); @@ -1365,17 +1404,19 @@ void service_discovery_impl::process_serviceentry( its_major, its_minor, _unicast_flag); break; case entry_type_e::OFFER_SERVICE: - process_offerservice_serviceentry(its_service, its_instance, - its_major, its_minor, its_ttl, - its_reliable_address, its_reliable_port, - its_unreliable_address, its_unreliable_port, _resubscribes); + if (_accept_offers) { + process_offerservice_serviceentry(its_service, its_instance, + its_major, its_minor, its_ttl, + its_reliable_address, its_reliable_port, + its_unreliable_address, its_unreliable_port, _resubscribes); + } break; case entry_type_e::UNKNOWN: default: VSOMEIP_ERROR << "Unsupported serviceentry type"; } - } else { + } else if (_accept_offers) { std::shared_ptr<request> its_request = find_request(its_service, its_instance); if (its_request) { std::lock_guard<std::mutex> its_lock(requested_mutex_); @@ -1491,7 +1532,7 @@ void service_discovery_impl::process_offerservice_serviceentry( if (its_subscription->is_acknowledged()) { if (its_offer_type == remote_offer_type_e::UNRELIABLE) { - if (its_unreliable && its_unreliable->is_connected()) { + if (its_unreliable && its_unreliable->is_established()) { // 28 = 16 (subscription) + 12 (option) check_space(28); const std::size_t options_size_before = @@ -1514,7 +1555,7 @@ void service_discovery_impl::process_offerservice_serviceentry( } } } else if (its_offer_type == remote_offer_type_e::RELIABLE) { - if (its_reliable && its_reliable->is_connected()) { + if (its_reliable && its_reliable->is_established()) { // 28 = 16 (subscription) + 12 (option) check_space(28); const std::size_t options_size_before = @@ -1544,8 +1585,8 @@ void service_discovery_impl::process_offerservice_serviceentry( } } else if (its_offer_type == remote_offer_type_e::RELIABLE_UNRELIABLE) { if (its_reliable && its_unreliable && - its_reliable->is_connected() && - its_unreliable->is_connected()) { + its_reliable->is_established() && + its_unreliable->is_established()) { // 40 = 16 (subscription) + 2x12 (option) check_space(40); const std::size_t options_size_before = @@ -1566,7 +1607,7 @@ void service_discovery_impl::process_offerservice_serviceentry( static_cast<std::uint16_t>( _resubscribes->back().first - 40); } - } else if (its_reliable && !its_reliable->is_connected()) { + } else if (its_reliable && !its_reliable->is_established()) { its_client.second->set_tcp_connection_established(false); // restart TCP endpoint if not connected its_reliable->restart(); @@ -1597,7 +1638,7 @@ void service_discovery_impl::process_offerservice_serviceentry( } // restart TCP endpoint if not connected - if (its_reliable && !its_reliable->is_connected()) { + if (its_reliable && !its_reliable->is_established()) { its_reliable->restart(); } } @@ -1735,14 +1776,14 @@ void service_discovery_impl::on_endpoint_connected( its_subscription->get_endpoint(true)); const std::shared_ptr<const endpoint> its_unreliable_endpoint( its_subscription->get_endpoint(false)); - if(its_reliable_endpoint && its_reliable_endpoint->is_connected()) { + if(its_reliable_endpoint && its_reliable_endpoint->is_established()) { if(its_reliable_endpoint.get() == _endpoint.get()) { // mark tcp as established its_subscription->set_tcp_connection_established(true); } } - if(its_unreliable_endpoint && its_unreliable_endpoint->is_connected()) { - if(its_reliable_endpoint.get() == _endpoint.get()) { + if(its_unreliable_endpoint && its_unreliable_endpoint->is_established()) { + if(its_unreliable_endpoint.get() == _endpoint.get()) { // mark udp as established its_subscription->set_udp_connection_established(true); } @@ -2633,7 +2674,10 @@ void service_discovery_impl::serialize_and_send( _message->set_session(its_session.first); _message->set_reboot_flag(its_session.second); if(!serializer_->serialize(_message.get())) { - VSOMEIP_ERROR << "service_discovery_impl::serialize_and_send: serialization error."; + boost::system::error_code ec; + VSOMEIP_ERROR << "service_discovery_impl::serialize_and_send: serialization error." + << " Remote: " << _address.to_string(ec) << " session: 0x" + << std::hex << its_session.first; return; } if (host_->send_to(endpoint_definition::get(_address, port_, reliable_, _message->get_service(), _message->get_instance()), @@ -2660,7 +2704,10 @@ void service_discovery_impl::stop_ttl_timer() { void service_discovery_impl::check_ttl(const boost::system::error_code &_error) { if (!_error) { - host_->update_routing_info(ttl_timer_runtime_); + { + std::lock_guard<std::mutex> its_lock(check_ttl_mutex_); + host_->update_routing_info(ttl_timer_runtime_); + } start_ttl_timer(); } } @@ -2752,7 +2799,7 @@ void service_discovery_impl::send_subscriptions(service_t _service, instance_t _ = its_runtime->create_message(); if (its_reliable && its_unreliable) { - if (its_reliable->is_connected() && its_unreliable->is_connected()) { + if (its_reliable->is_established() && its_unreliable->is_established()) { insert_subscription(its_message, _service, _instance, found_eventgroup.first, found_client->second, its_offer_type); @@ -2764,7 +2811,7 @@ void service_discovery_impl::send_subscriptions(service_t _service, instance_t _ } } else { if(_reliable) { - if(endpoint->is_connected()) { + if(endpoint->is_established()) { insert_subscription(its_message, _service, _instance, found_eventgroup.first, found_client->second, its_offer_type); @@ -2775,7 +2822,7 @@ void service_discovery_impl::send_subscriptions(service_t _service, instance_t _ found_client->second->set_tcp_connection_established(false); } } else { - if (endpoint->is_connected()) { + if (endpoint->is_established()) { insert_subscription(its_message, _service, _instance, found_eventgroup.first, found_client->second, its_offer_type); @@ -2827,47 +2874,39 @@ void service_discovery_impl::stop_subscription_expiration_timer_unlocked() { void service_discovery_impl::expire_subscriptions(const boost::system::error_code &_error) { if (!_error) { - next_subscription_expiration_ = host_->expire_subscriptions(); + next_subscription_expiration_ = host_->expire_subscriptions(false); start_subscription_expiration_timer(); } } bool service_discovery_impl::check_ipv4_address( - boost::asio::ip::address its_address) { + const boost::asio::ip::address& its_address) const { //Check unallowed ipv4 address bool is_valid = true; - std::shared_ptr<configuration> its_configuration = - host_->get_configuration(); - - if(its_configuration) { - boost::asio::ip::address_v4::bytes_type its_unicast_address = - its_configuration.get()->get_unicast_address().to_v4().to_bytes(); - boost::asio::ip::address_v4::bytes_type endpoint_address = - its_address.to_v4().to_bytes(); - - //same address as unicast address of DUT not allowed - if(its_unicast_address - == endpoint_address) { - VSOMEIP_ERROR << "Subscribers endpoint IP address is same as DUT's address! : " - << its_address.to_string(); - is_valid = false; - } - - // first 3 triples must match - its_unicast_address[3] = 0x00; - endpoint_address[3] = 0x00; - if(its_unicast_address - != endpoint_address) { -#if 1 - VSOMEIP_ERROR<< "First 3 triples of subscribers endpoint IP address are not valid!"; -#endif + static const boost::asio::ip::address_v4::bytes_type its_unicast_address = + unicast_.to_v4().to_bytes(); + const boost::asio::ip::address_v4::bytes_type endpoint_address = + its_address.to_v4().to_bytes(); + static const boost::asio::ip::address_v4::bytes_type its_netmask = + configuration_->get_netmask().to_v4().to_bytes(); + + //same address as unicast address of DUT not allowed + if (its_unicast_address == endpoint_address) { + VSOMEIP_ERROR << "Subscriber's IP address is same as host's address! : " + << its_address; + is_valid = false; + } else { + const std::uint32_t self = VSOMEIP_BYTES_TO_LONG(its_unicast_address[0], + its_unicast_address[1], its_unicast_address[2], its_unicast_address[3]); + const std::uint32_t remote = VSOMEIP_BYTES_TO_LONG(endpoint_address[0], + endpoint_address[1], endpoint_address[2], endpoint_address[3]); + const std::uint32_t netmask = VSOMEIP_BYTES_TO_LONG(its_netmask[0], + its_netmask[1], its_netmask[2], its_netmask[3]); + if ((self & netmask) != (remote & netmask)) { + VSOMEIP_ERROR<< "Subscriber's IP isn't in the same subnet as host's IP: " + << its_address; is_valid = false; - - } else { -#if 0 - VSOMEIP_INFO << "First 3 triples of subscribers endpoint IP address are valid!"; -#endif } } return is_valid; @@ -3012,7 +3051,7 @@ void service_discovery_impl::on_offer_debounce_timer_expired( for (services_t::iterator its_service = collected_offers_.begin(); its_service != collected_offers_.end(); its_service++) { for (auto its_instance : its_service->second) { - if (!host_->get_configuration()->is_someip( + if (!configuration_->is_someip( its_service->first, its_instance.first)) { non_someip_services.push_back(its_service); } @@ -3286,6 +3325,8 @@ bool service_discovery_impl::serialize_and_send_messages( void service_discovery_impl::stop_offer_service( service_t _service, instance_t _instance, std::shared_ptr<serviceinfo> _info) { + std::lock_guard<std::mutex> its_lock(offer_mutex_); + _info->set_ttl(0); bool stop_offer_required(false); // delete from initial phase offers { @@ -3420,19 +3461,11 @@ bool service_discovery_impl::last_offer_shorter_half_offer_delay_ago() { bool service_discovery_impl::check_source_address( const boost::asio::ip::address &its_source_address) const { bool is_valid = true; - std::shared_ptr<configuration> its_configuration = - host_->get_configuration(); - - if(its_configuration) { - boost::asio::ip::address its_unicast_address = - its_configuration.get()->get_unicast_address(); - // check if source address is same as nodes unicast address - if(its_unicast_address - == its_source_address) { - VSOMEIP_ERROR << "Source address of message is same as DUT's unicast address! : " - << its_source_address.to_string(); - is_valid = false; - } + // check if source address is same as nodes unicast address + if(unicast_ == its_source_address) { + VSOMEIP_ERROR << "Source address of message is same as DUT's unicast address! : " + << its_source_address.to_string(); + is_valid = false; } return is_valid; } @@ -3502,8 +3535,10 @@ void service_discovery_impl::update_subscription_expiration_timer( const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); stop_subscription_expiration_timer_unlocked(); + + std::unique_lock<std::mutex> its_message_lock(_message->get_message_lock()); for (const auto &entry : _message->get_entries()) { - if (entry->get_type() == entry_type_e::SUBSCRIBE_EVENTGROUP_ACK + if (entry && entry->get_type() == entry_type_e::SUBSCRIBE_EVENTGROUP_ACK && entry->get_ttl()) { const std::chrono::steady_clock::time_point its_expiration = now + std::chrono::seconds( @@ -3792,5 +3827,15 @@ service_discovery_impl::get_eventgroups_requiring_initial_events( return its_acks; } +void service_discovery_impl::register_offer_acceptance_handler( + vsomeip::offer_acceptance_handler_t _handler) { + offer_acceptance_handler_ = _handler; +} + +void service_discovery_impl::register_reboot_notification_handler( + reboot_notification_handler_t _handler) { + reboot_notification_handler_ = _handler; +} + } // namespace sd } // namespace vsomeip diff --git a/implementation/tracing/include/channel_impl.hpp b/implementation/tracing/include/channel_impl.hpp new file mode 100644 index 0000000..9034657 --- /dev/null +++ b/implementation/tracing/include/channel_impl.hpp @@ -0,0 +1,62 @@ +// Copyright (C) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef VSOMEIP_TRACING_CHANNEL_IMPL_HPP_ +#define VSOMEIP_TRACING_CHANNEL_IMPL_HPP_ + +#include <atomic> +#include <functional> +#include <map> +#include <mutex> +#include <string> + +#include <vsomeip/trace.hpp> + +namespace vsomeip { +namespace trace { + +typedef std::function<bool (service_t, instance_t, method_t)> filter_func_t; + +class channel_impl : public channel { +public: + channel_impl(const std::string &_id, const std::string &_name); + + std::string get_id() const; + std::string get_name() const; + + filter_id_t add_filter( + const match_t &_match, + bool _is_positive); + + filter_id_t add_filter( + const std::vector<match_t> &_matches, + bool _is_positive); + + filter_id_t add_filter( + const match_t &_from, const match_t &_to, + bool _is_positive); + + void remove_filter( + filter_id_t _id); + + bool matches(service_t _service, instance_t _instance, method_t _method); + +private: + filter_id_t add_filter_intern(filter_func_t _func, bool _is_positive); + + std::string id_; + std::string name_; + + std::atomic<filter_id_t> current_filter_id_; + + std::map<filter_id_t, filter_func_t> positive_; + std::map<filter_id_t, filter_func_t> negative_; + std::mutex mutex_; // protects positive_ & negative_ +}; + +} // namespace trace +} // namespace vsomeip + +#endif // VSOMEIP_TRACING_CHANNEL_IMPL_HPP_ diff --git a/implementation/tracing/include/connector_impl.hpp b/implementation/tracing/include/connector_impl.hpp new file mode 100644 index 0000000..3234fed --- /dev/null +++ b/implementation/tracing/include/connector_impl.hpp @@ -0,0 +1,81 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef VSOMEIP_TC_TRACE_CONNECTOR_HPP +#define VSOMEIP_TC_TRACE_CONNECTOR_HPP + +#ifdef USE_DLT +#include <dlt/dlt.h> +#endif + +#include <mutex> +#include <vector> +#include <map> + +#include <boost/shared_ptr.hpp> + +#include <vsomeip/primitive_types.hpp> +#include <vsomeip/export.hpp> +#include <vsomeip/trace.hpp> + +#include "enumeration_types.hpp" +#include "header.hpp" +#include "../../endpoints/include/buffer.hpp" + +namespace vsomeip { + +namespace cfg { + struct trace; +} + +namespace trace { + +class channel_impl; + +class connector_impl : public connector { +public: + VSOMEIP_EXPORT static std::shared_ptr<connector_impl> get(); + + VSOMEIP_EXPORT connector_impl(); + VSOMEIP_EXPORT virtual ~connector_impl(); + + VSOMEIP_EXPORT void configure(const std::shared_ptr<cfg::trace> &_configuration); + VSOMEIP_EXPORT void reset(); + + VSOMEIP_EXPORT void set_enabled(const bool _enabled); + VSOMEIP_EXPORT bool is_enabled() const; + + VSOMEIP_EXPORT void set_sd_enabled(const bool _sd_enabled); + VSOMEIP_EXPORT bool is_sd_enabled() const; + + VSOMEIP_EXPORT bool is_sd_message(const byte_t *_data, uint16_t _data_size) const; + + VSOMEIP_EXPORT std::shared_ptr<channel> add_channel(const std::string &_id, + const std::string &_description); + VSOMEIP_EXPORT bool remove_channel(const std::string &_id); + VSOMEIP_EXPORT std::shared_ptr<channel> get_channel(const std::string &_id) const; + + VSOMEIP_EXPORT void trace(const byte_t *_header, uint16_t _header_size, + const byte_t *_data, uint16_t _data_size); + +private: + bool is_enabled_; + bool is_sd_enabled_; + bool is_initialized_; + + std::map<std::string, std::shared_ptr<channel_impl>> channels_; + mutable std::mutex channels_mutex_; + +#ifdef USE_DLT + std::map<std::string, std::shared_ptr<DltContext>> contexts_; + mutable std::mutex contexts_mutex_; +#endif + +}; + +} // namespace trace +} // namespace vsomeip + +#endif // VSOMEIP_TC_TRACE_CONNECTOR_HPP diff --git a/implementation/tracing/include/defines.hpp b/implementation/tracing/include/defines.hpp index aadb44f..2325259 100644 --- a/implementation/tracing/include/defines.hpp +++ b/implementation/tracing/include/defines.hpp @@ -7,7 +7,9 @@ #define TRACING_INCLUDE_DEFINES_HPP_ #define VSOMEIP_TC_DEFAULT_CHANNEL_NAME "Trace Connector Network Logging" -#define VSOMEIP_TC_DEFAULT_CHANNEL_ID "TC" #define VSOMEIP_TC_DEFAULT_FILTER_TYPE "positive" +#define VSOMEIP_TC_INSTANCE_POS_MIN 8 +#define VSOMEIP_TC_INSTANCE_POS_MAX 9 + #endif /* TRACING_INCLUDE_DEFINES_HPP_ */ diff --git a/implementation/tracing/include/enumeration_types.hpp b/implementation/tracing/include/enumeration_types.hpp index 82ed3c4..352ddd3 100644 --- a/implementation/tracing/include/enumeration_types.hpp +++ b/implementation/tracing/include/enumeration_types.hpp @@ -3,24 +3,18 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#ifndef VSOMEIP_TC_ENUMERATION_TYPES_HPP -#define VSOMEIP_TC_ENUMERATION_TYPES_HPP +#ifndef VSOMEIP_TRACE_ENUMERATION_TYPES_HPP +#define VSOMEIP_TRACE_ENUMERATION_TYPES_HPP namespace vsomeip { -namespace tc { - -enum class filter_criteria_e : uint8_t { - SERVICES = 0x00, - METHODS = 0x01, - CLIENTS = 0x02, -}; +namespace trace { enum class filter_type_e : uint8_t { NEGATIVE = 0x00, POSITIVE = 0x01 }; -} // namespace tc +} // namespace trace } // namespace vsomeip #endif // VSOMEIP_TC_ENUMERATION_TYPES_HPP diff --git a/implementation/tracing/include/trace_header.hpp b/implementation/tracing/include/header.hpp index 11ccbdd..55ec73e 100644 --- a/implementation/tracing/include/trace_header.hpp +++ b/implementation/tracing/include/header.hpp @@ -3,8 +3,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#ifndef VSOMEIP_TC_TRACE_HEADER_HPP -#define VSOMEIP_TC_TRACE_HEADER_HPP +#ifndef VSOMEIP_TRACE_HEADER_HPP +#define VSOMEIP_TRACE_HEADER_HPP #include <memory> @@ -18,7 +18,7 @@ namespace vsomeip { class endpoint; -namespace tc { +namespace trace { enum class protocol_e : uint8_t { local = 0x0, @@ -27,7 +27,7 @@ enum class protocol_e : uint8_t { unknown = 0xFF }; -struct trace_header { +struct header { bool prepare(const std::shared_ptr<endpoint> &_endpoint, bool _is_sending, instance_t _instance); bool prepare(const endpoint* _endpoint, bool _is_sending, @@ -39,7 +39,7 @@ struct trace_header { byte_t data_[VSOMEIP_TRACE_HEADER_SIZE]; }; -} // namespace tc +} // namespace trace } // namespace vsomeip -#endif // VSOMEIP_TC_TRACE_HEADER_HPP +#endif // VSOMEIP_TRACE_HEADER_HPP diff --git a/implementation/tracing/include/trace_connector.hpp b/implementation/tracing/include/trace_connector.hpp deleted file mode 100644 index ef36e29..0000000 --- a/implementation/tracing/include/trace_connector.hpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef VSOMEIP_TC_TRACE_CONNECTOR_HPP -#define VSOMEIP_TC_TRACE_CONNECTOR_HPP - -#include <vsomeip/primitive_types.hpp> -#include <vsomeip/export.hpp> -#include <boost/shared_ptr.hpp> -#include <mutex> -#include <vector> -#include <map> - -#ifdef USE_DLT -#include <dlt/dlt.h> -#endif - -#include "enumeration_types.hpp" -#include "trace_header.hpp" -#include "../../endpoints/include/buffer.hpp" - -namespace vsomeip -{ -namespace tc -{ - -class trace_connector { -public: - typedef uint16_t filter_expression_t; - typedef std::vector<filter_expression_t> filter_expressions_t; - typedef std::map<filter_criteria_e, filter_expressions_t> filter_rule_map_t; - typedef std::pair<filter_type_e, filter_rule_map_t> filter_rule_t; - - typedef std::map<trace_channel_t, std::string> channels_t; - typedef std::map<trace_channel_t, filter_rule_t> filter_rules_t; - -#ifdef USE_DLT - typedef std::map<trace_channel_t, DltContext*> dlt_contexts_t; -#endif - - VSOMEIP_EXPORT static std::shared_ptr<trace_connector> get(); - - VSOMEIP_EXPORT trace_connector(); - VSOMEIP_EXPORT virtual ~trace_connector(); - - VSOMEIP_EXPORT void init(); - VSOMEIP_EXPORT void reset(); - - VSOMEIP_EXPORT void set_enabled(const bool _enabled); - VSOMEIP_EXPORT bool is_enabled() const; - - VSOMEIP_EXPORT void set_sd_enabled(const bool _enabled); - VSOMEIP_EXPORT bool is_sd_enabled() const; - - VSOMEIP_EXPORT bool is_sd_message(const byte_t *_data, uint16_t _data_size) const; - - VSOMEIP_EXPORT bool add_channel(const trace_channel_t &_id,const std::string &_name); - VSOMEIP_EXPORT bool remove_channel(const trace_channel_t &_id); - - VSOMEIP_EXPORT bool add_filter_rule(const trace_channel_t &_channel_id, - const filter_rule_t _filter_rule); - VSOMEIP_EXPORT bool add_filter_expression(const trace_channel_t &_channel_id, - const filter_criteria_e _criteria, - const filter_expression_t _expression); - VSOMEIP_EXPORT bool change_filter_expressions(const trace_channel_t &_channel_id, - const filter_criteria_e _criteria, - const filter_expressions_t _expressions); - VSOMEIP_EXPORT bool remove_filter_rule(const trace_channel_t &_channel_id); - - VSOMEIP_EXPORT void trace(const byte_t *_header, uint16_t _header_size, - const byte_t *_data, uint16_t _data_size); - - VSOMEIP_EXPORT channels_t get_channels(); - VSOMEIP_EXPORT filter_rules_t get_filter_rules(); - VSOMEIP_EXPORT filter_rule_t get_filter_rule(const trace_channel_t &_channel_id); - -private: - - bool apply_filter_rules(const byte_t *_data, const uint16_t _data_size, - std::vector<trace_channel_t> &_send_msg_over_channels); - - bool filter_expressions_match(const filter_criteria_e _criteria, - const filter_expressions_t _expressions, - const byte_t *_data, const uint16_t _data_size); - - bool is_enabled_; - bool is_sd_enabled_; - bool is_initialized_; - - channels_t channels_; - filter_rules_t filter_rules_; - -#ifdef USE_DLT - dlt_contexts_t dlt_contexts_; -#endif - - std::mutex channels_mutex_; - std::mutex filter_rules_mutex_; - std::mutex dlt_contexts_mutex; -}; - -} // namespace tc -} // namespace vsomeip - -#endif // VSOMEIP_TC_TRACE_CONNECTOR_HPP diff --git a/implementation/tracing/src/channel_impl.cpp b/implementation/tracing/src/channel_impl.cpp new file mode 100644 index 0000000..58f67e6 --- /dev/null +++ b/implementation/tracing/src/channel_impl.cpp @@ -0,0 +1,273 @@ +// Copyright (C) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include "../include/channel_impl.hpp" +#include "../../logging/include/logger.hpp" + +namespace vsomeip { +namespace trace { + +const filter_id_t FILTER_ID_ERROR(0); + +channel_impl::channel_impl(const std::string &_id, const std::string &_name) + : id_(_id), name_(_name), current_filter_id_(1) { +} + +std::string channel_impl::get_id() const { + return id_; +} + +std::string channel_impl::get_name() const { + return name_; +} + +filter_id_t channel_impl::add_filter( + const match_t &_match, bool _is_positive) { + + // Create a filter function + std::function<bool (service_t, instance_t, method_t)> its_filter_func; + if (std::get<0>(_match) != ANY_SERVICE) { + if (std::get<1>(_match) != ANY_INSTANCE) { + if (std::get<2>(_match) != ANY_METHOD) { + its_filter_func + = [_match](service_t _s, instance_t _i, method_t _m) { + return (std::get<0>(_match) == _s + && std::get<1>(_match) == _i + && std::get<2>(_match) == _m); + }; + } else { + its_filter_func + = [_match](service_t _s, instance_t _i, method_t) { + return (std::get<0>(_match) == _s + && std::get<1>(_match) == _i); + }; + } + } else { + if (std::get<2>(_match) != ANY_METHOD) { + its_filter_func + = [_match](service_t _s, instance_t, method_t _m) { + return (std::get<0>(_match) == _s + && std::get<1>(_match) == _m); + }; + } else { + its_filter_func + = [_match](service_t _s, instance_t, method_t) { + return (std::get<0>(_match) == _s); + }; + } + } + } else { + if (std::get<1>(_match) != ANY_INSTANCE) { + if (std::get<2>(_match) != ANY_METHOD) { + its_filter_func + = [_match](service_t, instance_t _i, method_t _m) { + return (std::get<1>(_match) == _i + && std::get<2>(_match) == _m); + }; + } else { + its_filter_func + = [_match](service_t, instance_t _i, method_t) { + return (std::get<1>(_match) == _i); + }; + } + } else { + if (std::get<2>(_match) != ANY_METHOD) { + its_filter_func + = [_match](service_t, instance_t, method_t _m) { + return (std::get<2>(_match) == _m); + }; + } else { + its_filter_func + = [](service_t, instance_t, method_t) { + return true; + }; + } + } + } + + return add_filter_intern(its_filter_func, _is_positive); +} + +filter_id_t channel_impl::add_filter( + const std::vector<match_t> &_matches, bool _is_positive) { + bool has_service(false); + bool has_instance(false); + bool has_method(false); + + for (auto m : _matches) { + if (std::get<0>(m) != ANY_SERVICE) has_service = true; + if (std::get<1>(m) != ANY_INSTANCE) has_instance = true; + if (std::get<2>(m) != ANY_METHOD) has_method = true; + } + + // Create a filter function + std::function<bool (service_t, instance_t, method_t)> its_filter_func; + if (has_service) { + if (has_instance) { + if (has_method) { + its_filter_func + = [_matches](service_t _s, instance_t _i, method_t _m) { + for (const auto &m : _matches) { + if ((std::get<0>(m) == _s || std::get<0>(m) == ANY_SERVICE) + && (std::get<1>(m) == _i || std::get<1>(m) == ANY_INSTANCE) + && (std::get<2>(m) == _m || std::get<2>(m) == ANY_METHOD)) { + return true; + } + } + return false; + }; + } else { + its_filter_func + = [_matches](service_t _s, instance_t _i, method_t) { + for (const auto &m : _matches) { + if ((std::get<0>(m) == _s || std::get<0>(m) == ANY_SERVICE) + && (std::get<1>(m) == _i || std::get<1>(m) == ANY_INSTANCE)) { + return true; + } + } + return false; + }; + } + } else { + if (has_method) { + its_filter_func + = [_matches](service_t _s, instance_t, method_t _m) { + for (const auto &m : _matches) { + if ((std::get<0>(m) == _s || std::get<0>(m) == ANY_SERVICE) + && (std::get<2>(m) == _m || std::get<2>(m) == ANY_METHOD)) { + return true; + } + } + return false; + }; + } else { + its_filter_func + = [_matches](service_t _s, instance_t, method_t) { + for (auto &m : _matches) { + if (std::get<0>(m) == _s || std::get<0>(m) == ANY_SERVICE) { + return true; + } + } + return false; + }; + } + } + } else { + if (has_instance) { + if (has_method) { + its_filter_func + = [_matches](service_t, instance_t _i, method_t _m) { + for (auto &m : _matches) { + if ((std::get<1>(m) == _i || std::get<1>(m) == ANY_INSTANCE) + && (std::get<2>(m) == _m || std::get<2>(m) == ANY_METHOD)) { + return true; + } + } + return false; + }; + } else { + its_filter_func + = [_matches](service_t, instance_t _i, method_t) { + for (auto &m : _matches) { + if (std::get<1>(m) == _i || std::get<1>(m) == ANY_INSTANCE) { + return true; + } + } + return false; + }; + } + } else { + if (has_method) { + its_filter_func + = [_matches](service_t, instance_t, method_t _m) { + for (auto &m : _matches) { + if (std::get<2>(m) == _m || std::get<2>(m) == ANY_METHOD) { + return true; + } + } + return false; + }; + } else { + its_filter_func + = [](service_t, instance_t, method_t) { + return true; + }; + } + } + } + + return add_filter_intern(its_filter_func, _is_positive); +} + +filter_id_t channel_impl::add_filter( + const match_t &_from, const match_t &_to, bool _is_positive) { + + // Check usage of ANY_* which is forbidden here + if (std::get<0>(_from) == ANY_SERVICE || + std::get<1>(_from) == ANY_INSTANCE || + std::get<2>(_from) == ANY_METHOD || + std::get<0>(_to) == ANY_SERVICE || + std::get<1>(_to) == ANY_INSTANCE || + std::get<2>(_to) == ANY_METHOD) { + VSOMEIP_ERROR << "Trace filter configuration error: " + "You must not use wildcards in range filters."; + return FILTER_ID_ERROR; + } + + std::function<bool (service_t, instance_t, method_t)> its_filter_func + = [_from, _to](service_t _s, instance_t _i, method_t _m) { + return (std::get<0>(_from) <= _s && _s <= std::get<0>(_to) + && std::get<1>(_from) <= _i && _i <= std::get<1>(_to) + && std::get<2>(_from) <= _m && _m <= std::get<2>(_to)); + }; + + return add_filter_intern(its_filter_func, _is_positive); +} + +void channel_impl::remove_filter(filter_id_t _id) { + std::lock_guard<std::mutex> its_lock(mutex_); + positive_.erase(_id); + negative_.erase(_id); +} + +filter_id_t channel_impl::add_filter_intern(filter_func_t _func, bool _is_positive) { + filter_id_t its_id = current_filter_id_.fetch_add(1); + + std::lock_guard<std::mutex> its_lock(mutex_); + if (_is_positive) + positive_[its_id] = _func; + else + negative_[its_id] = _func; + + return its_id; +} + +bool channel_impl::matches( + service_t _service, instance_t _instance, method_t _method) { + std::lock_guard<std::mutex> its_lock(mutex_); + + // If a negative filter matches --> drop! + for (auto &its_filter : negative_) { + if (its_filter.second(_service, _instance, _method)) { + return false; + } + } + + // If no positive filter is defined --> forward! + if (positive_.size() == 0) + return true; + + // If a positive filter matches --> forward! + for (auto &its_filter : positive_) { + if (its_filter.second(_service, _instance, _method)) { + return true; + } + } + + // drop! + return false; +} + +} // namespace trace +} // namespace vsomeip diff --git a/implementation/tracing/src/connector_impl.cpp b/implementation/tracing/src/connector_impl.cpp new file mode 100644 index 0000000..f8655db --- /dev/null +++ b/implementation/tracing/src/connector_impl.cpp @@ -0,0 +1,226 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <vsomeip/constants.hpp> + +#include "../include/channel_impl.hpp" +#include "../include/connector_impl.hpp" +#include "../include/defines.hpp" +#include "../../configuration/include/internal.hpp" +#include "../../configuration/include/trace.hpp" +#include "../../logging/include/logger.hpp" +#include "../../utility/include/byteorder.hpp" + +namespace vsomeip { +namespace trace { + +const char *VSOMEIP_TC_DEFAULT_CHANNEL_ID = "TC"; + +std::shared_ptr<connector_impl> connector_impl::get() { + static std::shared_ptr<connector_impl> instance = std::make_shared<connector_impl>(); + return instance; +} + +connector_impl::connector_impl() : + is_enabled_(false), + is_sd_enabled_(false), + is_initialized_(false) { + + channels_[VSOMEIP_TC_DEFAULT_CHANNEL_ID] + = std::make_shared<channel_impl>(VSOMEIP_TC_DEFAULT_CHANNEL_ID, + VSOMEIP_TC_DEFAULT_CHANNEL_NAME); +#ifdef USE_DLT + std::shared_ptr<DltContext> its_default_context + = std::make_shared<DltContext>(); + + contexts_[VSOMEIP_TC_DEFAULT_CHANNEL_ID] = its_default_context; + DLT_REGISTER_CONTEXT_LL_TS(*(its_default_context.get()), + VSOMEIP_TC_DEFAULT_CHANNEL_ID, VSOMEIP_TC_DEFAULT_CHANNEL_NAME, + DLT_LOG_INFO, DLT_TRACE_STATUS_ON); +#endif +} + +connector_impl::~connector_impl() { + reset(); +} + +void connector_impl::configure(const std::shared_ptr<cfg::trace> &_configuration) { + if (_configuration) { + is_enabled_ = _configuration->is_enabled_; + is_sd_enabled_ = _configuration->is_sd_enabled_; + } + + if (is_enabled_) { // No need to create filters if tracing is disabled! + for (auto &its_channel : _configuration->channels_) { + if (!add_channel(its_channel->id_, its_channel->name_)) { + VSOMEIP_ERROR << "Channel " << its_channel->id_ + << " has multiple definitions."; + } + } + + for (auto &its_filter : _configuration->filters_) { + for (auto &its_channel : its_filter->channels_) { + std::shared_ptr<channel> its_channel_ptr = get_channel(its_channel); + if (its_channel_ptr) { + if (its_filter->is_range_) { + its_channel_ptr->add_filter(its_filter->matches_[0], + its_filter->matches_[1], its_filter->is_positive_); + } else { + its_channel_ptr->add_filter(its_filter->matches_, + its_filter->is_positive_); + } + } + } + } + } + + VSOMEIP_INFO << "vsomeip tracing " + << (is_enabled_ ? "enabled " : "not enabled. ") + << ". vsomeip service discovery tracing " + << (is_sd_enabled_ ? "enabled " : "not enabled. "); +} + +void connector_impl::reset() { +#ifdef USE_DLT + std::lock_guard<std::mutex> its_contexts_lock(contexts_mutex_); + contexts_.clear(); +#endif + // reset to default + std::lock_guard<std::mutex> its_lock_channels(channels_mutex_); + channels_.clear(); +} + +void connector_impl::set_enabled(const bool _enabled) { + is_enabled_ = _enabled; +} + +bool connector_impl::is_enabled() const { + return is_enabled_; +} + +void connector_impl::set_sd_enabled(const bool _sd_enabled) { + is_sd_enabled_ = _sd_enabled; +} + +bool connector_impl::is_sd_enabled() const { + return is_sd_enabled_; +} + +bool connector_impl::is_sd_message(const byte_t *_data, uint16_t _data_size) const { + if (VSOMEIP_METHOD_POS_MAX < _data_size) { + return (_data[VSOMEIP_SERVICE_POS_MIN] == 0xFF && _data[VSOMEIP_SERVICE_POS_MAX] == 0xFF && + _data[VSOMEIP_METHOD_POS_MIN] == 0x81 && _data[VSOMEIP_METHOD_POS_MAX] == 0x00); + } + return false; +} + +std::shared_ptr<channel> connector_impl::add_channel( + const trace_channel_t &_id, const std::string &_name) { + std::lock_guard<std::mutex> its_channels_lock(channels_mutex_); + + // check whether we already know the requested channel + if (channels_.find(_id) != channels_.end()) + return nullptr; + + // create new channel + std::shared_ptr<channel_impl> its_channel + = std::make_shared<channel_impl>(_id, _name); + + // add channel + channels_[_id] = its_channel; + + // register context +#ifdef USE_DLT + std::lock_guard<std::mutex> its_contexts_lock(contexts_mutex_); + std::shared_ptr<DltContext> its_context = std::make_shared<DltContext>(); + contexts_[_id] = its_context; + DLT_REGISTER_CONTEXT_LL_TS(*(its_context.get()), _id.c_str(), _name.c_str(), + DLT_LOG_INFO, DLT_TRACE_STATUS_ON); +#endif + + return its_channel; +} + +bool connector_impl::remove_channel(const trace_channel_t &_id) { + if (_id == VSOMEIP_TC_DEFAULT_CHANNEL_ID) { + // the default channel can not be removed + return false; + } + + std::lock_guard<std::mutex> its_channels_lock(channels_mutex_); + bool has_removed = (channels_.erase(_id) == 1); + if (has_removed) { + // unregister context +#ifdef USE_DLT + std::lock_guard<std::mutex> its_contexts_lock(contexts_mutex_); + auto its_context = contexts_.find(_id); + if (its_context != contexts_.end()) { + DLT_UNREGISTER_CONTEXT(*(its_context->second.get())); + } +#endif + } + + return true; +} + +std::shared_ptr<channel> connector_impl::get_channel(const std::string &_id) const { + std::lock_guard<std::mutex> its_channels_lock(channels_mutex_); + auto its_channel = channels_.find(_id); + return (its_channel != channels_.end() ? its_channel->second : nullptr); +} + +void connector_impl::trace(const byte_t *_header, uint16_t _header_size, + const byte_t *_data, uint16_t _data_size) { +#ifdef USE_DLT + if (!is_enabled_) + return; + + if (_data_size == 0) + return; // no data + + if (is_sd_message(_data, _data_size) && !is_sd_enabled_) + return; // tracing of service discovery messages is disabled! + + service_t its_service = VSOMEIP_BYTES_TO_WORD( + _data[VSOMEIP_SERVICE_POS_MIN], + _data[VSOMEIP_SERVICE_POS_MAX]); + + // Instance is not part of the SOME/IP header, read it from the trace + // header + instance_t its_instance = VSOMEIP_BYTES_TO_WORD( + _header[VSOMEIP_TC_INSTANCE_POS_MIN], + _header[VSOMEIP_TC_INSTANCE_POS_MAX]); + + method_t its_method = VSOMEIP_BYTES_TO_WORD( + _data[VSOMEIP_METHOD_POS_MIN], + _data[VSOMEIP_METHOD_POS_MAX]); + + // Forward to channel if the filter set of the channel allows + std::lock_guard<std::mutex> its_channels_lock(channels_mutex_); + std::lock_guard<std::mutex> its_contexts_lock(contexts_mutex_); + for (auto its_channel : channels_) { + if (its_channel.second->matches(its_service, its_instance, its_method)) { + auto its_context = contexts_.find(its_channel.second->get_id()); + if (its_context != contexts_.end()) { + DLT_TRACE_NETWORK_SEGMENTED(*(its_context->second.get()), + DLT_NW_TRACE_IPC, + _header_size, static_cast<void *>(const_cast<byte_t *>(_header)), + _data_size, static_cast<void *>(const_cast<byte_t *>(_data))); + } else { + // This should never happen! + VSOMEIP_ERROR << "tracing: found channel without DLT context!"; + } + } + } +#else + (void)_header; + (void)_header_size; + (void)_data; + (void)_data_size; +#endif +} + +} // namespace trace +} // namespace vsomeip diff --git a/implementation/tracing/src/trace_header.cpp b/implementation/tracing/src/header.cpp index 3ae1b10..2f4f3f8 100644 --- a/implementation/tracing/src/trace_header.cpp +++ b/implementation/tracing/src/header.cpp @@ -5,20 +5,20 @@ #include <cstring> -#include "../include/trace_header.hpp" +#include "../include/header.hpp" #include "../../endpoints/include/endpoint.hpp" #include "../../endpoints/include/client_endpoint.hpp" #include "../../utility/include/byteorder.hpp" namespace vsomeip { -namespace tc { +namespace trace { -bool trace_header::prepare(const std::shared_ptr<endpoint> &_endpoint, +bool header::prepare(const std::shared_ptr<endpoint> &_endpoint, bool _is_sending, instance_t _instance) { return prepare(_endpoint.get(), _is_sending, _instance); } -bool trace_header::prepare(const endpoint *_endpoint, bool _is_sending, +bool header::prepare(const endpoint *_endpoint, bool _is_sending, instance_t _instance) { boost::asio::ip::address its_address; unsigned short its_port(0); @@ -51,9 +51,9 @@ bool trace_header::prepare(const endpoint *_endpoint, bool _is_sending, return true; } -void trace_header::prepare(const boost::asio::ip::address_v4 &_address, - std::uint16_t _port, protocol_e _protocol, - bool _is_sending, instance_t _instance) { +void header::prepare(const boost::asio::ip::address_v4 &_address, + std::uint16_t _port, protocol_e _protocol, + bool _is_sending, instance_t _instance) { unsigned long its_address_as_long = _address.to_ulong(); data_[0] = VSOMEIP_LONG_BYTE3(its_address_as_long); data_[1] = VSOMEIP_LONG_BYTE2(its_address_as_long); @@ -67,5 +67,5 @@ void trace_header::prepare(const boost::asio::ip::address_v4 &_address, data_[9] = VSOMEIP_WORD_BYTE0(_instance); } -} // namespace tc +} // namespace trace } // namespace vsomeip diff --git a/implementation/tracing/src/trace_connector.cpp b/implementation/tracing/src/trace_connector.cpp deleted file mode 100644 index 93f7d6e..0000000 --- a/implementation/tracing/src/trace_connector.cpp +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include "../include/trace_connector.hpp" - -#include <vsomeip/constants.hpp> - -#include "../include/defines.hpp" -#include "../../configuration/include/internal.hpp" - -namespace vsomeip { -namespace tc { - -std::shared_ptr<trace_connector> trace_connector::get() { - static std::shared_ptr<trace_connector> instance = std::make_shared<trace_connector>(); - return instance; -} - -trace_connector::trace_connector() : - is_enabled_(false), - is_sd_enabled_(false), - is_initialized_(false), - channels_(), - filter_rules_() - { - channels_.insert(std::make_pair(VSOMEIP_TC_DEFAULT_CHANNEL_ID, VSOMEIP_TC_DEFAULT_CHANNEL_NAME)); -} - -trace_connector::~trace_connector() { - reset(); -} - -void trace_connector::init() { -#ifdef USE_DLT - if(!is_initialized_) { - // register channels/contexts - std::lock_guard<std::mutex> lock(dlt_contexts_mutex); - for(auto it = channels_.begin(); it != channels_.end(); ++it) { - DltContext *dlt_context = new DltContext(); - dlt_contexts_.insert(std::make_pair(it->first, dlt_context)); - DLT_REGISTER_CONTEXT_LL_TS(*dlt_context, it->first.c_str(), it->second.c_str(), DLT_LOG_INFO, DLT_TRACE_STATUS_ON); - } - } -#endif - is_initialized_ = true; -} - -void trace_connector::reset() { - std::lock_guard<std::mutex> its_lock_channels(channels_mutex_); - std::lock_guard<std::mutex> its_lock_filter_rules(filter_rules_mutex_); - std::lock_guard<std::mutex> its_lock_dlt_contexts(dlt_contexts_mutex); - -#ifdef USE_DLT - // unregister channels/contexts - for(auto it = dlt_contexts_.begin(); it != dlt_contexts_.end(); ++it) { - DLT_UNREGISTER_CONTEXT(*it->second); - delete it->second; - } - dlt_contexts_.clear(); -#endif - - // reset to default - - channels_.clear(); - channels_.insert(std::make_pair(VSOMEIP_TC_DEFAULT_CHANNEL_ID, VSOMEIP_TC_DEFAULT_CHANNEL_NAME)); - - filter_rules_.clear(); - - is_initialized_ = false; -} - -void trace_connector::set_enabled(const bool _enabled) { - is_enabled_ = _enabled; -} - -bool trace_connector::is_enabled() const { - return is_enabled_; -} - -void trace_connector::set_sd_enabled(const bool _sd_enabled) { - is_sd_enabled_ = _sd_enabled; -} - -bool trace_connector::is_sd_enabled() const { - return is_sd_enabled_; -} - -bool trace_connector::is_sd_message(const byte_t *_data, uint16_t _data_size) const { - if (VSOMEIP_METHOD_POS_MAX < _data_size) { - return (_data[VSOMEIP_SERVICE_POS_MIN] == 0xFF && _data[VSOMEIP_SERVICE_POS_MAX] == 0xFF && - _data[VSOMEIP_METHOD_POS_MIN] == 0x81 && _data[VSOMEIP_METHOD_POS_MAX] == 0x00); - } - return false; -} - -bool trace_connector::add_channel(const trace_channel_t &_id, const std::string &_name) { - std::lock_guard<std::mutex> its_lock_channels(channels_mutex_); - std::lock_guard<std::mutex> its_lock_dlt_contexts(dlt_contexts_mutex); - - bool channel_inserted = false; - bool dlt_context_registered = false; - - // add channel - channel_inserted = channels_.insert(std::make_pair(_id, _name)).second; - - // register context -#ifdef USE_DLT - if(channel_inserted) { - DltContext *dlt_context = new DltContext(); - dlt_context_registered = dlt_contexts_.insert(std::make_pair(_id, dlt_context)).second; - DLT_REGISTER_CONTEXT_LL_TS(*dlt_context, _id.c_str(), _name.c_str(), DLT_LOG_INFO, DLT_TRACE_STATUS_ON); - } -#endif - - return (channel_inserted && dlt_context_registered); -} - -bool trace_connector::remove_channel(const trace_channel_t &_id) { - - if(_id == VSOMEIP_TC_DEFAULT_CHANNEL_ID) { - // the default channel can not be removed - return false; - } - - std::lock_guard<std::mutex> its_lock_channels(channels_mutex_); - std::lock_guard<std::mutex> its_lock_filter_rules(filter_rules_mutex_); - std::lock_guard<std::mutex> its_lock_dlt_contexts(dlt_contexts_mutex); - - bool channel_removed = false; - bool dlt_context_unregistered = false; - - // remove channel - channel_removed = (channels_.erase(_id) == 1); - if(channel_removed) { - - // unregister context -#ifdef USE_DLT - auto it = dlt_contexts_.find(_id); - if(it != dlt_contexts_.end()) { - DltContext *dlt_context = it->second; - DLT_UNREGISTER_CONTEXT(*dlt_context); - dlt_context_unregistered = true; - } -#endif - - // remove filter - filter_rules_.erase(_id); - } - return (channel_removed && dlt_context_unregistered); -} - -bool trace_connector::add_filter_rule(const trace_channel_t &_channel_id, - const filter_rule_t _filter_rule) { - std::lock_guard<std::mutex> its_lock_filter_rules(filter_rules_mutex_); - std::lock_guard<std::mutex> its_lock_channels(channels_mutex_); - - // find channel - auto it = channels_.find(_channel_id); - if(it != channels_.end()) { - // add filter rule - return filter_rules_.insert(std::make_pair(_channel_id, _filter_rule)).second; - } - return false; -} - -bool trace_connector::add_filter_expression(const trace_channel_t &_channel_id, - const filter_criteria_e _criteria, - const filter_expression_t _expression) { - std::lock_guard<std::mutex> its_lock_filter_rules(filter_rules_mutex_); - - // find filter rule - auto it_filter_rules = filter_rules_.find(_channel_id); - if(it_filter_rules != filter_rules_.end()) { - filter_rule_t its_filter_rule = it_filter_rules->second; - - // find filter criteria - auto it_filter_rule_map = its_filter_rule.second.find(_criteria); - if(it_filter_rule_map != its_filter_rule.second.end()) { - // add expression - it_filter_rule_map->second.push_back(_expression); - return true; - } - } - return false; -} - -bool trace_connector::change_filter_expressions(const trace_channel_t &_channel_id, - const filter_criteria_e _criteria, - const filter_expressions_t _expressions) { - std::lock_guard<std::mutex> its_lock_filter_rules(filter_rules_mutex_); - - // find filter rule - auto it_filter_rules = filter_rules_.find(_channel_id); - if(it_filter_rules != filter_rules_.end()) { - filter_rule_t its_filter_rule = it_filter_rules->second; - - // find filter criteria - auto it_filter_rule_map = its_filter_rule.second.find(_criteria); - if(it_filter_rule_map != its_filter_rule.second.end()) { - // change expressions - it_filter_rule_map->second = _expressions; - return true; - } - } - return false; -} - -bool trace_connector::remove_filter_rule(const trace_channel_t &_channel_id) { - std::lock_guard<std::mutex> its_lock_filter_rules(filter_rules_mutex_); - - auto it = filter_rules_.find(_channel_id); - if(it != filter_rules_.end()) { - return (filter_rules_.erase(_channel_id) == 1); - } - return false; -} - -void trace_connector::trace(const byte_t *_header, uint16_t _header_size, - const byte_t *_data, uint16_t _data_size) { -#ifdef USE_DLT - if(!is_enabled_) - return; - - std::lock_guard<std::mutex> its_lock_filter_rules(filter_rules_mutex_); - std::lock_guard<std::mutex> its_lock_channels(channels_mutex_); - std::lock_guard<std::mutex> its_lock_dlt_contexts(dlt_contexts_mutex); - - if (_data_size == 0) - return; // no data - - if (is_sd_message(_data, _data_size) && !is_sd_enabled_) - return; // tracing of service discovery messages is disabled! - - // check if filter rules match - std::vector<trace_channel_t> its_channels; - if (apply_filter_rules(_data, _data_size, its_channels)) { - // send message over 'its_channels' - for(auto channel_id : its_channels) { - //find dlt context - auto it = dlt_contexts_.find(channel_id); - if (it != dlt_contexts_.end()) { - DltContext *dlt_context = it->second; - DLT_TRACE_NETWORK_SEGMENTED(*dlt_context, - DLT_NW_TRACE_IPC, - _header_size, static_cast<void *>(const_cast<byte_t *>(_header)), - _data_size, static_cast<void *>(const_cast<byte_t *>(_data))); - } - } - } -#else - (void)_header; - (void)_header_size; - (void)_data; - (void)_data_size; -#endif -} - -trace_connector::channels_t trace_connector::get_channels() { - std::lock_guard<std::mutex>its_lock_channels(channels_mutex_); - return channels_; -} - -trace_connector::filter_rules_t trace_connector::get_filter_rules() { - std::lock_guard<std::mutex> its_lock_filters(filter_rules_mutex_); - return filter_rules_; -} - -trace_connector::filter_rule_t trace_connector::get_filter_rule(const trace_channel_t &_channel_id) { - std::lock_guard<std::mutex> its_lock_filters(filter_rules_mutex_); - - // find filter - auto it = filter_rules_.find(_channel_id); - if (it != filter_rules_.end()) { - return filter_rules_[_channel_id]; - } else { - return filter_rule_t(); - } -} - -bool trace_connector::apply_filter_rules(const byte_t *_data, uint16_t _data_size, - std::vector<trace_channel_t> &_send_msg_over_channels) { - _send_msg_over_channels.clear(); - if (filter_rules_.size() == 0) { - // no filter rules -> send message over all channels to DLT - for(auto it=channels_.begin(); it !=channels_.end(); ++it) - _send_msg_over_channels.push_back(it->first); - return true; - } - - // loop through filter rules - for(auto it_filter_rules = filter_rules_.begin(); it_filter_rules != filter_rules_.end(); ++it_filter_rules) { - trace_channel_t its_channel = it_filter_rules->first; - filter_rule_t its_filter_rule = it_filter_rules->second; - - // apply filter rule - bool trace_message = true; - filter_type_e its_filter_type = its_filter_rule.first; - for(auto it_filter_rule_map = its_filter_rule.second.begin(); - it_filter_rule_map != its_filter_rule.second.end() && trace_message; - ++it_filter_rule_map) { - - filter_criteria_e its_criteria = it_filter_rule_map->first; - auto &its_filter_expressions = it_filter_rule_map->second; - - if(its_filter_expressions.size() != 0) { - // check if filter expressions of filter criteria match - const bool filter_rule_matches = filter_expressions_match(its_criteria, its_filter_expressions, _data, _data_size); - trace_message = !(filter_rule_matches && its_filter_type == filter_type_e::NEGATIVE); - } - } - - if(trace_message) { - //filter rule matches -> send message over 'its_channel' to DLT - _send_msg_over_channels.push_back(its_channel); - } - } - return (_send_msg_over_channels.size() != 0); -} - -bool trace_connector::filter_expressions_match( - const filter_criteria_e _criteria, const filter_expressions_t _expressions, - const byte_t *_data, uint16_t _data_size) { - - // ignore empty filter expressions - if (_expressions.size() == 0) { - return true; - } - - // extract criteria from message - bool is_successful(false); - byte_t first = 0; - byte_t second = 0; - switch (_criteria) { - case filter_criteria_e::SERVICES: - if (VSOMEIP_SERVICE_POS_MAX < _data_size) { - first = _data[VSOMEIP_SERVICE_POS_MIN]; - second = _data[VSOMEIP_SERVICE_POS_MAX]; - is_successful = true; - } - break; - case filter_criteria_e::METHODS: - if (VSOMEIP_SERVICE_POS_MAX < _data_size) { - first = _data[VSOMEIP_METHOD_POS_MIN]; - second = _data[VSOMEIP_METHOD_POS_MAX]; - is_successful = true; - } - break; - case filter_criteria_e::CLIENTS: - if (VSOMEIP_CLIENT_POS_MAX < _data_size) { - first = _data[VSOMEIP_CLIENT_POS_MIN]; - second = _data[VSOMEIP_CLIENT_POS_MAX]; - is_successful = true; - } - break; - default: - break; - } - - // if extraction is successful, filter - if (is_successful) { - bool filter_expressions_matches = false; - for (auto it_expressions = _expressions.begin(); - it_expressions != _expressions.end() && !filter_expressions_matches; - ++it_expressions) { - filter_expression_t its_filter_expression = *it_expressions; - uint16_t its_message_value = 0; - - its_message_value = (uint16_t)((its_message_value << 8) + first); - its_message_value = (uint16_t)((its_message_value << 8) + second); - - filter_expressions_matches = (its_filter_expression == its_message_value); - } - return filter_expressions_matches; - } - return false; -} - -} // namespace tc -} // namespace vsomeip diff --git a/implementation/utility/include/utility.hpp b/implementation/utility/include/utility.hpp index 34979a6..c5d8afe 100644 --- a/implementation/utility/include/utility.hpp +++ b/implementation/utility/include/utility.hpp @@ -11,13 +11,23 @@ #include <set> #include <atomic> +#ifdef _WIN32 + #include <stdlib.h> + #define bswap_16(x) _byteswap_ushort(x) + #define bswap_32(x) _byteswap_ulong(x) +#else + #include <byteswap.h> +#endif + #include <vsomeip/enumeration_types.hpp> #include <vsomeip/message.hpp> #include "criticalsection.hpp" +#include "../../../implementation/configuration/include/policy.hpp" namespace vsomeip { class configuration; +struct policy; class utility { public: @@ -124,13 +134,46 @@ public: || _type == message_type_e::MT_UNKNOWN); } + static inline bool is_valid_return_code(return_code_e _code) { + return (_code == return_code_e::E_OK + || _code == return_code_e::E_NOT_OK + || _code == return_code_e::E_UNKNOWN_SERVICE + || _code == return_code_e::E_UNKNOWN_METHOD + || _code == return_code_e::E_NOT_READY + || _code == return_code_e::E_NOT_REACHABLE + || _code == return_code_e::E_TIMEOUT + || _code == return_code_e::E_WRONG_PROTOCOL_VERSION + || _code == return_code_e::E_WRONG_INTERFACE_VERSION + || _code == return_code_e::E_MALFORMED_MESSAGE + || _code == return_code_e::E_WRONG_MESSAGE_TYPE); + } + + VSOMEIP_EXPORT static bool parse_policy(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_uid, uint32_t &_gid, ::std::shared_ptr<policy> &_policy); + VSOMEIP_EXPORT static bool parse_uid_gid(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_uid, uint32_t &_gid); + private: - static bool is_bigger_last_assigned_client_id(client_t _client, std::uint16_t _diagnosis_mask); - static void set_max_assigned_client_id_without_diagnosis(client_t _client); static void check_client_id_consistency(); + static std::uint16_t get_max_number_of_clients(std::uint16_t _diagnosis_max); + static inline bool parse_range(const byte_t* &_buffer, uint32_t &_buffer_size, uint16_t &_first, uint16_t &_last); + static inline bool parse_id(const byte_t* &_buffer, uint32_t &_buffer_size, uint16_t &_id); + static inline bool get_struct_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length); + static inline bool get_union_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length); + static inline bool get_array_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length); + static inline bool is_range(const byte_t* &_buffer, uint32_t &_buffer_size); + static inline bool parse_id_item(const byte_t* &_buffer, uint32_t& parsed_ids_bytes, ranges_t& its_ranges, uint32_t &_buffer_size); static std::atomic<std::uint16_t> its_configuration_refs__; static std::uint16_t* used_client_ids__; + + static const uint8_t uid_width_; + static const uint8_t gid_width_; + static const uint8_t id_width_; + static const uint8_t range_width_; + static const uint8_t skip_union_length_ ; + static const uint8_t skip_union_type_ ; + static const uint8_t skip_union_length_type_ ; + static const uint8_t skip_struct_length_; + static const uint8_t skip_array_length_; }; } // namespace vsomeip diff --git a/implementation/utility/src/utility.cpp b/implementation/utility/src/utility.cpp index 6b633ea..9919ea8 100644 --- a/implementation/utility/src/utility.cpp +++ b/implementation/utility/src/utility.cpp @@ -5,6 +5,7 @@ #ifdef _WIN32 #include <iostream> + #include <intrin.h> #else #include <dlfcn.h> #include <signal.h> @@ -101,9 +102,11 @@ static HANDLE its_descriptor(INVALID_HANDLE_VALUE); bool utility::auto_configuration_init(const std::shared_ptr<configuration> &_config) { std::unique_lock<CriticalSection> its_lock(its_local_configuration_mutex__); + const std::uint16_t its_max_clients = + get_max_number_of_clients(_config->get_diagnosis_mask()); const size_t its_shm_size = sizeof(configuration_data_t) + - static_cast<std::uint16_t>(~_config->get_diagnosis_mask()) * sizeof(client_t); + (its_max_clients + 1) * sizeof(client_t); #ifdef _WIN32 if (its_configuration_refs__ > 0) { assert(configuration_data_mutex != INVALID_HANDLE_VALUE); @@ -187,9 +190,9 @@ bool utility::auto_configuration_init(const std::shared_ptr<configuration> &_con the_configuration_data__->client_base_ = static_cast<unsigned short>((_config->get_diagnosis_address() << 8) & _config->get_diagnosis_mask()); - the_configuration_data__->max_clients_ = static_cast<std::uint16_t>(~_config->get_diagnosis_mask()); + the_configuration_data__->max_clients_ = its_max_clients; the_configuration_data__->max_used_client_ids_index_ = 1; - the_configuration_data__->max_assigned_client_id_without_diagnosis_ = 0x00; + the_configuration_data__->max_assigned_client_id_ = 0x00; the_configuration_data__->routing_manager_host_ = 0x0000; // the clientid array starts right after the routing_manager_host_ struct member used_client_ids__ = reinterpret_cast<unsigned short*>( @@ -232,11 +235,13 @@ bool utility::auto_configuration_init(const std::shared_ptr<configuration> &_con // shm is already mapped into the process its_configuration_refs__++; } else { - const mode_t previous_mask(::umask(static_cast<mode_t>(_config->get_umask()))); - int its_descriptor = shm_open(utility::get_shm_name(_config).c_str(), O_RDWR | O_CREAT | O_EXCL, + int its_descriptor = shm_open(utility::get_shm_name(_config).c_str(), O_RDWR | O_CREAT | O_EXCL, static_cast<mode_t>(_config->get_permissions_shm())); - ::umask(previous_mask); - if (its_descriptor > -1) { + if (its_descriptor > -1) { + if (-1 == chmod(std::string("/dev/shm").append(utility::get_shm_name(_config)).c_str(), + static_cast<mode_t>(_config->get_permissions_uds()))) { + VSOMEIP_ERROR << __func__ << ": chmod: " << strerror(errno); + } if (-1 == ftruncate(its_descriptor, its_shm_size)) { VSOMEIP_ERROR << "utility::auto_configuration_init: " "ftruncate failed: " << std::strerror(errno); @@ -278,9 +283,9 @@ bool utility::auto_configuration_init(const std::shared_ptr<configuration> &_con the_configuration_data__->client_base_ = static_cast<unsigned short>((_config->get_diagnosis_address() << 8) & _config->get_diagnosis_mask()); - the_configuration_data__->max_clients_ = static_cast<std::uint16_t>(~_config->get_diagnosis_mask()); + the_configuration_data__->max_clients_ = its_max_clients; the_configuration_data__->max_used_client_ids_index_ = 1; - the_configuration_data__->max_assigned_client_id_without_diagnosis_ = 0x00; + the_configuration_data__->max_assigned_client_id_ = 0x00; the_configuration_data__->routing_manager_host_ = 0x0000; // the clientid array starts right after the routing_manager_host_ struct member used_client_ids__ = reinterpret_cast<unsigned short*>( @@ -307,10 +312,18 @@ bool utility::auto_configuration_init(const std::shared_ptr<configuration> &_con } } } else if (errno == EEXIST) { - const mode_t previous_mask(::umask(static_cast<mode_t>(_config->get_umask()))); its_descriptor = shm_open(utility::get_shm_name(_config).c_str(), O_RDWR, static_cast<mode_t>(_config->get_permissions_shm())); - ::umask(previous_mask); + + int retry_count = 8; + std::chrono::milliseconds retry_delay = std::chrono::milliseconds(10); + while (its_descriptor == -1 && retry_count-- > 0) { + std::this_thread::sleep_for(retry_delay); + its_descriptor = shm_open(utility::get_shm_name(_config).c_str(), O_RDWR, + static_cast<mode_t>(_config->get_permissions_shm())); + retry_delay *= 2; + } + if (-1 == its_descriptor) { VSOMEIP_ERROR << "utility::auto_configuration_init: " "shm_open failed: " << std::strerror(errno); @@ -376,8 +389,6 @@ bool utility::auto_configuration_init(const std::shared_ptr<configuration> &_con void utility::auto_configuration_exit(client_t _client, const std::shared_ptr<configuration> &_config) { std::unique_lock<CriticalSection> its_lock(its_local_configuration_mutex__); - const size_t its_shm_size = sizeof(configuration_data_t) + - static_cast<std::uint16_t>(~_config->get_diagnosis_mask()) * sizeof(client_t); if (the_configuration_data__) { #ifdef _WIN32 // not manipulating data in shared memory, no need to take global mutex @@ -412,6 +423,10 @@ void utility::auto_configuration_exit(client_t _client, } if (its_configuration_refs__ == 0) { + const std::uint16_t its_max_clients = + get_max_number_of_clients(_config->get_diagnosis_mask()); + const size_t its_shm_size = sizeof(configuration_data_t) + + (its_max_clients + 1) * sizeof(client_t); if (-1 == ::munmap(the_configuration_data__, its_shm_size)) { VSOMEIP_ERROR << "utility::auto_configuration_exit: " "munmap failed: " << std::strerror(errno); @@ -586,16 +601,20 @@ client_t utility::request_client_id(const std::shared_ptr<configuration> &_confi if (use_autoconfig) { if (_client == ILLEGAL_CLIENT || is_used_client_id(_client, _config)) { - _client = the_configuration_data__->client_base_; + if (the_configuration_data__->max_assigned_client_id_ != 0x00) { + _client = the_configuration_data__->max_assigned_client_id_; + } else { + _client = the_configuration_data__->client_base_; + } } int increase_count = 0; - while (is_used_client_id(_client, _config) - || !is_bigger_last_assigned_client_id(_client, _config->get_diagnosis_mask()) - || _config->is_configured_client_id(_client)) { - if ((_client & the_configuration_data__->max_clients_) - + 1 > the_configuration_data__->max_clients_) { + + while (_client <= the_configuration_data__->max_assigned_client_id_ + || _config->is_configured_client_id(_client) + || is_used_client_id(_client, _config)) { + if (_client + 1 > used_client_ids__[0] + the_configuration_data__->max_clients_) { _client = the_configuration_data__->client_base_; - the_configuration_data__->max_assigned_client_id_without_diagnosis_ = 0; + the_configuration_data__->max_assigned_client_id_ = the_configuration_data__->client_base_; } else { _client++; increase_count++; @@ -614,7 +633,7 @@ client_t utility::request_client_id(const std::shared_ptr<configuration> &_confi } } } - set_max_assigned_client_id_without_diagnosis(_client); + the_configuration_data__->max_assigned_client_id_ = _client; } if (set_client_as_manager_host) { @@ -731,19 +750,343 @@ void utility::set_routing_manager_host(client_t _client) { #endif } -bool utility::is_bigger_last_assigned_client_id(client_t _client, std::uint16_t _diagnosis_mask) { - return _client - > ((the_configuration_data__->client_base_ & _diagnosis_mask) - + the_configuration_data__->max_assigned_client_id_without_diagnosis_); + +inline bool utility::get_struct_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) { + uint32_t its_length = 0; + bool length_field_deployed(false); + // [TR_SOMEIP_00080] d If the length of the length field is not specified, a length of 0 + // has to be assumed and no length field is in the message. + if (length_field_deployed) { + if (_buffer_size >= sizeof(uint32_t)) { + std::memcpy(&its_length, _buffer, sizeof(uint32_t)); + _length = bswap_32(its_length); + _buffer_size -= skip_struct_length_; + _buffer += skip_struct_length_; + return true; + } + } else { + _length = 0; + return true; + } + + return false; +} + +inline bool utility::get_union_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) { + uint32_t its_length = 0; + + // [TR_SOMEIP_00125] d If the Interface Specification does not specify the length of the + // length field for a union, 32 bit length of the length field shall be used. + if (_buffer_size >= sizeof(uint32_t)) { + std::memcpy(&its_length, _buffer, sizeof(uint32_t)); + _length = bswap_32(its_length); + _buffer_size -= skip_union_length_; + _buffer += skip_union_length_; + return true; + } + return false; } -void utility::set_max_assigned_client_id_without_diagnosis(client_t _client) { - const std::uint16_t its_client_id_without_diagnosis = - static_cast<std::uint16_t>(_client - & the_configuration_data__->max_clients_); - the_configuration_data__->max_assigned_client_id_without_diagnosis_ = - static_cast<std::uint16_t>(its_client_id_without_diagnosis - % the_configuration_data__->max_clients_); +inline bool utility::get_array_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) { + uint32_t its_length = 0; + + // [TR_SOMEIP_00106] d The layout of arrays with dynamic length basically is based on + // the layout of fixed length arrays. To determine the size of the array the serialization + // adds a length field (default length 32 bit) in front of the data, which counts the bytes + // of the array. The length does not include the size of the length field. Thus, when + // transporting an array with zero elements the length is set to zero. + if (_buffer_size >= sizeof(uint32_t)) { + std::memcpy(&its_length, _buffer, sizeof(uint32_t)); + _length = bswap_32(its_length); + _buffer_size -= skip_array_length_; + _buffer += skip_array_length_; + return true; + } + return false; +} + +inline bool utility::is_range(const byte_t* &_buffer, uint32_t &_buffer_size) { + uint32_t its_type = 0; + + // [TR_SOMEIP_00128] If the Interface Specification does not specify the length of the + // type field of a union, 32 bit length of the type field shall be used. + if (_buffer_size >= sizeof(uint32_t)) { + std::memcpy(&its_type, _buffer, sizeof(uint32_t)); + its_type = bswap_32(its_type); + _buffer_size -= skip_union_type_; + _buffer += skip_union_type_; + if (its_type == 0x02) { + return true; + } else { + return false; + } + } + return false; +} + +inline bool utility::parse_id_item(const byte_t* &_buffer, uint32_t& parsed_ids_bytes, ranges_t& its_ranges, uint32_t &_buffer_size) { + // get "union IdItem" length + uint32_t iditem_length = 0; + if (get_union_length(_buffer, _buffer_size, iditem_length)) { + // determine type of union + uint16_t its_first = 0; + uint16_t its_last = 0; + if (is_range(_buffer, _buffer_size)) { + // get range of instance IDs "struct IdRange" length + uint32_t range_length = 0; + if (get_struct_length(_buffer, _buffer_size, range_length)) { + // read first and last instance range + if (parse_range(_buffer, _buffer_size, its_first, its_last)) { + its_ranges.insert(std::make_pair(its_first, its_last)); + } else { + return false; + } + } + } else { + // a single instance ID + if (parse_id(_buffer, _buffer_size, its_first)) { + if (its_first != ANY_METHOD) { + if (its_first != 0x00) { + its_last = its_first; + its_ranges.insert(std::make_pair(its_first, its_last)); + } + } else { + its_first = 0x01; + its_last = 0xFFFE; + its_ranges.insert(std::make_pair(its_first, its_last)); + } + } + } + parsed_ids_bytes += (skip_union_length_type_ + iditem_length); + } + return true; +} + +inline bool utility::parse_range(const byte_t* &_buffer, uint32_t &_buffer_size, uint16_t &_first, uint16_t &_last){ + uint16_t its_first = 0; + uint16_t its_last = 0; + + if (_buffer_size >= sizeof(uint16_t) * 2) { + if (parse_id(_buffer, _buffer_size, its_first)) { + _first = its_first; + } + if (parse_id(_buffer, _buffer_size, its_last)) { + _last = its_last; + } + if (_first != _last + && (_first == ANY_METHOD || _last == ANY_METHOD)) { + return false; + } + if (_first != 0x0 && _last != 0x00 + && _first <= _last) { + if (_first == ANY_METHOD && + _last == ANY_METHOD) { + _first = 0x01; + _last = 0xFFFE; + } + return true; + } else { + if (_first == 0x00 && _last > _first + && _last != ANY_METHOD) { + _first = 0x01; + return true; + } + return false; + } + } + return false; +} + +inline bool utility::parse_id(const byte_t* &_buffer, uint32_t &_buffer_size, uint16_t &_id) { + uint16_t its_id = 0; + if (_buffer_size >= sizeof(uint16_t)) { + std::memcpy(&its_id, _buffer, sizeof(uint16_t)); + _id = bswap_16(its_id); + _buffer_size -= id_width_; + _buffer += id_width_; + return true; + } + return false; +} + +bool utility::parse_uid_gid(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_uid, uint32_t &_gid) { + uint32_t its_uid = 0xffffffff; + uint32_t its_gid = 0xffffffff; + + if (_buffer_size >= sizeof(uint32_t) * 2) { + std::memcpy(&its_uid, _buffer, sizeof(uint32_t)); + _uid = bswap_32(its_uid); + + std::memcpy(&its_gid, _buffer + sizeof(uint32_t), sizeof(uint32_t)); + _gid = bswap_32(its_gid); + + _buffer_size -= (uid_width_ + gid_width_); + _buffer += (uid_width_ + gid_width_); + return true; + } + return false; +} + +bool utility::parse_policy(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_uid, uint32_t &_gid, ::std::shared_ptr<policy> &_policy) { + uint32_t its_uid = 0xffffffff; + uint32_t its_gid = 0xffffffff; + bool has_error(false); + + // get user ID String + if (parse_uid_gid(_buffer, _buffer_size, its_uid, its_gid)) { + _uid = its_uid; + _gid = its_gid; + + // policy elements + std::pair<uint32_t, uint32_t> its_uid_range, its_gid_range; + std::set<std::pair<uint32_t, uint32_t>> its_uids, its_gids; + + // fill uid and gid range + std::get<0>(its_uid_range) = its_uid; + std::get<1>(its_uid_range) = its_uid; + std::get<0>(its_gid_range) = its_gid; + std::get<1>(its_gid_range) = its_gid; + its_uids.insert(its_uid_range); + its_gids.insert(its_gid_range); + + _policy->ids_.insert(std::make_pair(its_uids, its_gids)); + _policy->allow_who_ = true; + _policy->allow_what_ = true; + + // get struct AclUpdate + uint32_t acl_length = 0; + if (get_struct_length(_buffer, _buffer_size, acl_length)) { + // get requests array length + uint32_t requests_array_length = 0; + if (get_array_length(_buffer, _buffer_size, requests_array_length)) { + // loop through requests array consisting of n x "struct Request" + uint32_t parsed_req_bytes = 0; + while (parsed_req_bytes + skip_struct_length_ <= requests_array_length) { + // get request struct length + uint32_t req_length = 0; + if (get_struct_length(_buffer, _buffer_size, req_length)) { + if (req_length != 0) + parsed_req_bytes += skip_struct_length_; + + uint16_t its_service_id = 0; + ids_t its_instance_method_ranges; + // get serviceID + if (!parse_id(_buffer, _buffer_size, its_service_id)) { + has_error = true; + } else { + if (its_service_id == 0x00 + || its_service_id == 0xFFFF) { + VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy with service ID: 0x" + << its_service_id << " is not allowed!"; + return false; + } + // add length of serviceID + parsed_req_bytes += id_width_; + } + + // get instances array length + uint32_t instances_array_length = 0; + if (get_array_length(_buffer, _buffer_size, instances_array_length)) { + // loop trough instances array consisting of n x "struct Instance" + uint32_t parsed_inst_bytes = 0; + while (parsed_inst_bytes + skip_struct_length_ <= instances_array_length) { + // get instance struct length + uint32_t inst_length = 0; + if (get_struct_length(_buffer, _buffer_size, inst_length)) { + if (inst_length != 0) + parsed_inst_bytes += skip_struct_length_; + + ranges_t its_instance_ranges; + ranges_t its_method_ranges; + // get "IdItem[] ids" array length + uint32_t ids_array_length = 0; + if (get_array_length(_buffer, _buffer_size, ids_array_length)) { + uint32_t parsed_ids_bytes = 0; + while (parsed_ids_bytes + skip_struct_length_ <= ids_array_length) { + if (!parse_id_item(_buffer, parsed_ids_bytes, its_instance_ranges, _buffer_size)) { + return false; + } + } + parsed_inst_bytes += (skip_array_length_ + ids_array_length); + } + // get "IdItem[] methods" array length + uint32_t methods_array_length = 0; + if (get_array_length(_buffer, _buffer_size, methods_array_length)) { + uint32_t parsed_method_bytes = 0; + while (parsed_method_bytes + skip_struct_length_ <= methods_array_length) { + if (!parse_id_item(_buffer, parsed_method_bytes, its_method_ranges, _buffer_size)) { + return false; + } + } + if (!its_instance_ranges.empty() && !its_method_ranges.empty()) { + its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); + } + parsed_inst_bytes += (skip_array_length_ + methods_array_length); + } + } + } + parsed_req_bytes += (skip_array_length_ + instances_array_length); + } + if (!its_instance_method_ranges.empty()) { + _policy->services_.insert( + std::make_pair(its_service_id, its_instance_method_ranges)); + } + } + } + } + // get offers array length + uint32_t offers_array_length = 0; + if (get_array_length(_buffer, _buffer_size, offers_array_length)){ + // loop through offers array + uint32_t parsed_offers_bytes = 0; + while (parsed_offers_bytes + skip_struct_length_ <= offers_array_length) { + // get service ID + uint16_t its_service_id = 0; + ranges_t its_instance_ranges; + // get serviceID + if (!parse_id(_buffer, _buffer_size, its_service_id)) { + has_error = true; + } else { + if (its_service_id == 0x00 + || its_service_id == 0xFFFF) { + VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy with service ID: 0x" + << its_service_id << " is not allowed!"; + return false; + } + // add length of serviceID + parsed_offers_bytes += id_width_; + } + + // get "IdItem[] ids" array length + uint32_t ids_array_length = 0; + if (get_array_length(_buffer, _buffer_size, ids_array_length)) { + uint32_t parsed_ids_bytes = 0; + while (parsed_ids_bytes + skip_struct_length_ <= ids_array_length) { + if (!parse_id_item(_buffer, parsed_ids_bytes, its_instance_ranges, _buffer_size)) { + return false; + } + } + parsed_offers_bytes += (skip_array_length_ + ids_array_length); + } + if (!its_instance_ranges.empty()) { + _policy->offers_.insert( + std::make_pair(its_service_id, its_instance_ranges)); + } + } + } + } else { + VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy with empty request / offer section is not allowed!"; + has_error = true; + } + } else { + VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy without UID / GID is not allowed!"; + has_error = true; + } + + if (!has_error) + return true; + else + return false; } void utility::check_client_id_consistency() { @@ -767,4 +1110,30 @@ void utility::check_client_id_consistency() { } } +std::uint16_t utility::get_max_number_of_clients(std::uint16_t _diagnosis_max) { + std::uint16_t its_max_clients(0); + const int bits_for_clients = +#ifdef _WIN32 + __popcnt( +#else + __builtin_popcount( +#endif + static_cast<std::uint16_t>(~_diagnosis_max)); + for (int var = 0; var < bits_for_clients; ++var) { + its_max_clients = static_cast<std::uint16_t>(its_max_clients | (1 << var)); + } + return its_max_clients; +} + +const uint8_t utility::uid_width_ = sizeof(uint32_t); +const uint8_t utility::gid_width_ = sizeof(uint32_t); +const uint8_t utility::id_width_ = sizeof(uint16_t); +const uint8_t utility::range_width_ = sizeof(uint32_t); + +const uint8_t utility::skip_union_length_ = sizeof(uint32_t); +const uint8_t utility::skip_union_type_ = sizeof(uint32_t); +const uint8_t utility::skip_union_length_type_ = sizeof(uint32_t) + sizeof(uint32_t); +const uint8_t utility::skip_struct_length_ = sizeof(uint32_t); +const uint8_t utility::skip_array_length_ = sizeof(uint32_t); + } // namespace vsomeip diff --git a/interface/vsomeip/application.hpp b/interface/vsomeip/application.hpp index 42bd534..a1296e8 100644 --- a/interface/vsomeip/application.hpp +++ b/interface/vsomeip/application.hpp @@ -23,6 +23,7 @@ namespace vsomeip { class configuration; class event; class payload; +struct policy; /** * \defgroup vsomeip @@ -944,7 +945,145 @@ public: * \param _handler Callback that shall be called. * */ - virtual void register_async_subscription_handler(service_t _service, instance_t _instance, eventgroup_t _eventgroup, async_subscription_handler_t _handler) = 0; + virtual void register_async_subscription_handler( + service_t _service, instance_t _instance, eventgroup_t _eventgroup, + async_subscription_handler_t _handler) = 0; + + /** + * \brief Enables or disables calling of registered offer acceptance + * handler for given IP address + * + * This method has only an effect when called on the application acting as + * routing manager + * + * \param _address IP address for which offer acceptance handler should be + * called + * \param _path Path which indicates need for offer acceptance + * \param _enable enable or disable calling of offer acceptance handler + */ + virtual void set_offer_acceptance_required(ip_address_t _address, + const std::string _path, + bool _enable) = 0; + + /** + * \brief Returns all configured IP addresses which require calling of + * registered offer acceptance handler + * + * This method has only an effect when called on the application acting as + * routing manager + * + * \return map with known IP addresses requiring offer acceptance handling + */ + typedef std::map<ip_address_t, std::string> offer_acceptance_map_type_t; + virtual offer_acceptance_map_type_t get_offer_acceptance_required() = 0; + + /** + * \brief Registers a handler which will be called upon reception of + * a remote offer with the offering ECU's IP address as parameter + * + * This method has only an effect when called on the application acting as + * routing manager + * + * \param _handler The handler to be called + */ + virtual void register_offer_acceptance_handler( + offer_acceptance_handler_t _handler) = 0; + + /** + * \brief Registers a handler which will be called upon detection of a + * reboot of a remote ECU with the remote ECU's IP address as a parameter + * + * This method has only an effect when called on the application acting as + * routing manager + * + * \param _handler The handler to be called + */ + virtual void register_reboot_notification_handler( + reboot_notification_handler_t _handler) = 0; + + /** + * \brief Registers a handler which will be called when the routing reached + * READY state. + * + * This method has only an effect when called on the application acting as + * routing manager + * + * \param _handler The handler to be called + */ + virtual void register_routing_ready_handler( + routing_ready_handler_t _handler) = 0; + + /** + * \brief Registers a handler which will be called when the routing state + * changes. + * + * This method has only an effect when called on the application acting as + * routing manager + * + * \param _handler The handler to be called + */ + virtual void register_routing_state_handler( + routing_state_handler_t _handler) = 0; + + /** + * \brief Update service configuration to offer a local service on the + * network as well + * + * This function is intended to take the necessary information to offer a + * service remotely if it was offered only locally beforehand. + * Precondition: The service must already be offered locally before + * calling this method. + * This function only has an effect if called on an application acting as + * routing manager. + * + * \param _service Service identifier + * \param _instance Instance identifier + * \param _port The port number on which the service should be offered + * \param _reliable Offer via TCP or UDP + * \param _magic_cookies_enabled Flag to enable magic cookies + * \param _offer Offer the service or stop offering it remotely + */ + virtual bool update_service_configuration(service_t _service, + instance_t _instance, + std::uint16_t _port, + bool _reliable, + bool _magic_cookies_enabled, + bool _offer) = 0; + + /** + * \brief Update security configuration of routing manager and all local clients + * The given handler gets called with "SU_SUCCESS" if the policy for UID + * and GID was updated or added successfully. If not all clients did confirm + * the update, SU_TIMEOUT is set. + * + * \param _uid UID of the policy + * \param _gid GID of the policy + * \param _policy The security policy to apply + * \param _payload serialized security policy object + * \param _handler handler which gets called after all clients have + * confirmed the policy update + */ + virtual void update_security_policy_configuration(uint32_t _uid, + uint32_t _gid, + std::shared_ptr<policy> _policy, + std::shared_ptr<payload> _payload, + security_update_handler_t _handler) = 0; + + /** + * \brief Remove a security configuration for routing manager and all local clients + * The given handler gets called with "SU_SUCCESS" if the policy for UID + * and GID was removed successfully. SU_UNKNOWN_USER_ID is set if the + * UID and GID was not found. If not all clients did confirm the removal, + * SU_TIMEOUT is set. + * + * \param _uid UID of the policy to remove + * \param _gid GID of the policy to remove + * \param _handler handler which gets called after all clients have + * confirmed the policy removal + */ + virtual void remove_security_policy_configuration(uint32_t _uid, + uint32_t _gid, + security_update_handler_t _handler) = 0; }; /** @} */ diff --git a/interface/vsomeip/constants.hpp b/interface/vsomeip/constants.hpp index d45f2c1..9dc33f0 100644 --- a/interface/vsomeip/constants.hpp +++ b/interface/vsomeip/constants.hpp @@ -37,6 +37,7 @@ const byte_t MAGIC_COOKIE_CLIENT_MESSAGE = 0x00; const byte_t MAGIC_COOKIE_SERVICE_MESSAGE = 0x80; const length_t MAGIC_COOKIE_SIZE = 0x00000008; const request_t MAGIC_COOKIE_REQUEST = 0xDEADBEEF; +const client_t MAGIC_COOKIE_NETWORK_BYTE_ORDER = 0xADDE; const protocol_version_t MAGIC_COOKIE_PROTOCOL_VERSION = 0x01; const interface_version_t MAGIC_COOKIE_INTERFACE_VERSION = 0x01; const message_type_e MAGIC_COOKIE_CLIENT_MESSAGE_TYPE = @@ -55,6 +56,7 @@ const event_t ANY_EVENT = 0xFFFF; const client_t ANY_CLIENT = 0xFFFF; const pending_subscription_id_t DEFAULT_SUBSCRIPTION = 0x0; +const pending_security_update_id_t DEFAULT_SECURITY_UPDATE_ID = 0x0; } // namespace vsomeip diff --git a/interface/vsomeip/defines.hpp b/interface/vsomeip/defines.hpp index 2a6af8b..0442fae 100644 --- a/interface/vsomeip/defines.hpp +++ b/interface/vsomeip/defines.hpp @@ -18,6 +18,7 @@ #define VSOMEIP_SOMEIP_HEADER_SIZE 8 #define VSOMEIP_SOMEIP_MAGIC_COOKIE_SIZE 8 +#define VSOMEIP_FULL_HEADER_SIZE 16 #define VSOMEIP_SERVICE_POS_MIN 0 #define VSOMEIP_SERVICE_POS_MAX 1 diff --git a/interface/vsomeip/enumeration_types.hpp b/interface/vsomeip/enumeration_types.hpp index 1f83b81..71ff38a 100644 --- a/interface/vsomeip/enumeration_types.hpp +++ b/interface/vsomeip/enumeration_types.hpp @@ -69,6 +69,13 @@ enum class offer_type_e : uint8_t { OT_ALL = 0x02, }; +enum class security_update_state_e : uint8_t { + SU_SUCCESS = 0x00, + SU_NOT_ALLOWED = 0x01, + SU_UNKNOWN_USER_ID = 0x02, + SU_INVALID_FORMAT = 0x03 +}; + } // namespace vsomeip #endif // VSOMEIP_ENUMERATION_TYPES_HPP diff --git a/interface/vsomeip/handler.hpp b/interface/vsomeip/handler.hpp index bcb2aa8..2fa57a8 100644 --- a/interface/vsomeip/handler.hpp +++ b/interface/vsomeip/handler.hpp @@ -27,6 +27,45 @@ typedef std::function< void (client_t, bool, std::function< void (const bool) > typedef std::function< void (const std::vector<std::pair<service_t, instance_t>> &_services) > offered_services_handler_t; typedef std::function< void () > watchdog_handler_t; +struct ip_address_t { + union { + ipv4_address_t v4_; + ipv6_address_t v6_; + } address_; + bool is_v4_; + + bool operator<(const ip_address_t& _other) const { + if (is_v4_ && _other.is_v4_) { + return address_.v4_ < _other.address_.v4_; + } else if (!is_v4_ && !_other.is_v4_) { + return address_.v6_ < _other.address_.v6_; + } else if (is_v4_ && !_other.is_v4_) { + return true; + } else { + return false; + } + } + + bool operator==(const ip_address_t& _other) const { + if (is_v4_ && _other.is_v4_) { + return address_.v4_ == _other.address_.v4_; + } else if (!is_v4_ && !_other.is_v4_) { + return address_.v6_ == _other.address_.v6_; + } else { + return false; + } + } + + bool operator!=(const ip_address_t& _other) const { + return !(*this == _other); + } + +}; +typedef std::function<bool(const ip_address_t&)> offer_acceptance_handler_t; +typedef std::function<void(const ip_address_t&)> reboot_notification_handler_t; +typedef std::function<void()> routing_ready_handler_t; +typedef std::function<void(routing_state_e)> routing_state_handler_t; +typedef std::function<void(security_update_state_e)> security_update_handler_t; } // namespace vsomeip diff --git a/interface/vsomeip/primitive_types.hpp b/interface/vsomeip/primitive_types.hpp index 9f6c84a..95192eb 100644 --- a/interface/vsomeip/primitive_types.hpp +++ b/interface/vsomeip/primitive_types.hpp @@ -44,6 +44,11 @@ typedef std::string trace_channel_t; typedef std::string trace_filter_type_t; typedef std::uint16_t pending_subscription_id_t; + +typedef std::uint32_t pending_remote_offer_id_t; + +typedef std::uint32_t pending_security_update_id_t; + } // namespace vsomeip #endif // VSOMEIP_PRIMITIVE_TYPES_HPP diff --git a/interface/vsomeip/trace.hpp b/interface/vsomeip/trace.hpp new file mode 100644 index 0000000..85cfd30 --- /dev/null +++ b/interface/vsomeip/trace.hpp @@ -0,0 +1,218 @@ +// Copyright (C) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#ifndef VSOMEIP_TRACE_HPP_ +#define VSOMEIP_TRACE_HPP_ + +#include <memory> +#include <vector> + +#include <vsomeip/constants.hpp> +#include <vsomeip/primitive_types.hpp> + +namespace vsomeip { +/** + * \defgroup vsomeip + * + * @{ + */ +namespace trace { + +/** + * \brief Unique identifier for trace filters. + */ +typedef uint32_t filter_id_t; + +/** + * \brief Error value. + */ +extern const filter_id_t FILTER_ID_ERROR; + +/** + * \brief The default channel id "TC". + */ +extern const char *VSOMEIP_TC_DEFAULT_CHANNEL_ID; + +/** + * \brief Filters contain at least one match that specified + * which messages are filtered. + */ +typedef std::tuple<service_t, instance_t, method_t> match_t; + +/** + * \brief Representation of a DLT trace channel. + * + * A trace channel contains one or more filters that specify the + * messages that are forwarded to the trace. + */ +class channel { +public: + virtual ~channel() {}; + + /** + * \brief Get the identifier of the channel. + * + * \return Channel identifier. + */ + virtual std::string get_id() const = 0; + + /** + * \brief Get the name of the channel. + * + * \return Channel name. + */ + virtual std::string get_name() const = 0; + + /** + * \brief Add a filter to the channel. + * + * Add a simple filter containing a single match. + * + * Note: The match is allowed to contain wildcards + * (ANY_INSTANCE, ANY_SERVICE, ANY_METHOD). + * + * \param _match The tuple specifying the matching messages. + * \param _is_positive True for positive filters, + * false for negative filters. Default value is true. + * + * \return Filter identifier of the added filter or + * FILTER_ID_ERROR if adding failed. + */ + virtual filter_id_t add_filter( + const match_t &_match, + bool _is_positive = true) = 0; + + /** + * \brief Add a filter to the channel. + * + * Add a filter containing a list of matches to the + * channel. The filter matches if at least on of the + * matches corresponds to a message. + * + * Note: The matches are allowed to contain wildcards + * (ANY_INSTANCE, ANY_SERVICE, ANY_METHOD). + * + * \param _matches List of tuples specifying the matching messages. + * \param _is_positive True for positive filters, + * false for negative filters. Default value is true. + * + * \return Filter identifier of the added filter or + * FILTER_ID_ERROR if adding failed. + */ + virtual filter_id_t add_filter( + const std::vector<match_t> &_matches, + bool _is_positive = true) = 0; + + /** + * \brief Add a filter to the channel. + * + * Add a filter containing a matches range to the + * channel. The filter matches if the message identifiers + * lie within the range specified by from and to. Thus, + * the messages service identifier is greater equal than + * the service identifier specified in from and less equal + * than the service identifier specified in to. + * + * Note: from and to must not contain wildcards + * (ANY_INSTANCE, ANY_SERVICE, ANY_METHOD). + * + * \param _from Tuples specifying the matching message with + * the smallest identifiers. + * \param _from Tuples specifying the matching message with + * the greatest identifiers. + * \param _is_positive True for positive filters, + * false for negative filters. Default value is true. + * + * \return Filter identifier of the added filter or + * FILTER_ID_ERROR if adding failed. + */ + virtual filter_id_t add_filter( + const match_t &_from, const match_t &_to, + bool _is_positive = true) = 0; + + /** + * \brief Remove a filter from the channel. + * + * Remove the filter with the given filter identifier + * from the channel. + * + * \param _id Filter identifier of the filter that shall + * be removed. + */ + virtual void remove_filter( + filter_id_t _id) = 0; +}; + +/** + * \brief Singleton class to connect to the DLT tracing. + * + * The main configuration class of the DLT tracing. It holds + * the trace channels which determine the messages that are + * forwarded to the trace. + */ +class connector { +public: + /** + * \brief Get access to the connector. + * + * \return Shared pointer to the singleton object. + */ + static std::shared_ptr<connector> get(); + + virtual ~connector() {}; + + /** + * \brief Add a trace channel to the connector. + * + * Creates a trace channel with the given identifier and name + * and adds it to the connector. + * + * \param _id Id of the trace channel. + * \param _name Name of the trace channel + * + * \return Shared pointer to the created trace channel or + * nullptr if the trace channel could not be created because + * another trace channel with the given identifier does + * already exist. + */ + virtual std::shared_ptr<channel> add_channel( + const std::string &_id, + const std::string &_name) = 0; + + /** + * \brief Remove a trace channel from the connector. + * + * Removes the trace channel with the given identifier from + * the connector. + * + * \param _id Identifier of a trace channel. + * + * \return True of the trace channel was removed, False if + * it could not be removed, because it is the default trace + * channel. + */ + virtual bool remove_channel( + const std::string &_id) = 0; + + /** + * \brief Get a trace channel from the connector. + * + * \param _id Identifier of the trace channel to be returned. + * Optional argument that is predefined with the identifier + * of the default trace channel. + * + * \return Shared pointer to the created trace channel or + * nullptr if the trace channel does not exist. + */ + virtual std::shared_ptr<channel> get_channel( + const std::string &_id = VSOMEIP_TC_DEFAULT_CHANNEL_ID) const = 0; +}; + +} // namespace trace + +/** @} */ + +} // namespace vsomeip + +#endif // VSOMEIP_CONSTANTS_HPP diff --git a/interface/vsomeip/vsomeip.hpp b/interface/vsomeip/vsomeip.hpp index 90940b2..1068b0b 100644 --- a/interface/vsomeip/vsomeip.hpp +++ b/interface/vsomeip/vsomeip.hpp @@ -16,5 +16,6 @@ #include <vsomeip/message.hpp> #include <vsomeip/payload.hpp> #include <vsomeip/runtime.hpp> +#include <vsomeip/trace.hpp> #endif // VSOMEIP_VSOMEIP_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d6e61bd..f8f1d5d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -596,8 +596,9 @@ endif() if (${TEST_SECURITY}) if(NOT ${TESTS_BAT}) set(TEST_SECURITY_NAME security_test) - set(TEST_SECURITY_SERVICE security_test_service) + set(TEST_SECURITY_CLIENT security_test_client) + add_executable(${TEST_SECURITY_SERVICE} security_tests/${TEST_SECURITY_SERVICE}.cpp) target_link_libraries(${TEST_SECURITY_SERVICE} vsomeip @@ -605,28 +606,91 @@ if (${TEST_SECURITY}) ${DL_LIBRARY} ${TEST_LINK_LIBRARIES} ) - + # Copy config file for service into $BUILDDIR/test - set(TEST_SECURITY_SERVICE_CONFIG_FILE ${TEST_SECURITY_NAME}_config.json) + set(TEST_SECURITY_LOCAL_CONFIG_FILE ${TEST_SECURITY_NAME}_local_config.json) configure_file( - ${PROJECT_SOURCE_DIR}/test/security_tests/conf/${TEST_SECURITY_SERVICE_CONFIG_FILE}.in - ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_CONFIG_FILE} + ${PROJECT_SOURCE_DIR}/test/security_tests/conf/${TEST_SECURITY_LOCAL_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_LOCAL_CONFIG_FILE} @ONLY) copy_to_builddir( - ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_CONFIG_FILE} - ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_SERVICE_CONFIG_FILE} + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_LOCAL_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_LOCAL_CONFIG_FILE} ${TEST_SECURITY_SERVICE} ) - - # Copy bashscript to start service into $BUILDDIR/test - set(TEST_SECURITY_SERVICE_START_SCRIPT ${TEST_SECURITY_NAME}_start.sh) + + # Copy service config file for external allow tests into $BUILDDIR/test + set(TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL ${TEST_SECURITY_NAME}_config_service_external_allow.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/security_tests/conf/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL}.in + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL} + @ONLY) copy_to_builddir( - ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_START_SCRIPT} - ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_SERVICE_START_SCRIPT} + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL} ${TEST_SECURITY_SERVICE} ) - - set(TEST_SECURITY_CLIENT security_test_client) + + # Copy client config file for external allow tests into $BUILDDIR/test + set(TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL ${TEST_SECURITY_NAME}_config_client_external_allow.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/security_tests/conf/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL}.in + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL} + ${TEST_SECURITY_SERVICE} + ) + + # Copy service config file for external deny tests into $BUILDDIR/test + set(TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL_DENY ${TEST_SECURITY_NAME}_config_service_external_deny.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/security_tests/conf/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL_DENY}.in + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL_DENY} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL_DENY} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_SERVICE_CONFIG_FILE_EXTERNAL_DENY} + ${TEST_SECURITY_SERVICE} + ) + + # Copy client config file for external deny tests into $BUILDDIR/test + set(TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL_DENY ${TEST_SECURITY_NAME}_config_client_external_deny.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/security_tests/conf/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL_DENY}.in + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL_DENY} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL_DENY} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_CLIENT_CONFIG_FILE_EXTERNAL_DENY} + ${TEST_SECURITY_SERVICE} + ) + + # Copy bashscript to start local test into $BUILDDIR/test + set(TEST_SECURITY_SERVICE_LOCAL_START_SCRIPT ${TEST_SECURITY_NAME}_local_start.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_SERVICE_LOCAL_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_SERVICE_LOCAL_START_SCRIPT} + ${TEST_SECURITY_SERVICE} + ) + + # Copy bashscript to start external tests (master) into $BUILDDIR/test + set(TEST_SECURITY_EXTERNAL_MASTER_START_SCRIPT ${TEST_SECURITY_NAME}_external_master_start.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_EXTERNAL_MASTER_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_EXTERNAL_MASTER_START_SCRIPT} + ${TEST_SECURITY_SERVICE} + ) + + # Copy bashscript to start external tests (slave) into $BUILDDIR/test + set(TEST_SECURITY_EXTERNAL_SLAVE_START_SCRIPT ${TEST_SECURITY_NAME}_external_slave_start.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_tests/${TEST_SECURITY_EXTERNAL_SLAVE_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_EXTERNAL_SLAVE_START_SCRIPT} + ${TEST_SECURITY_SERVICE} + ) + add_executable(${TEST_SECURITY_CLIENT} security_tests/${TEST_SECURITY_CLIENT}.cpp ) @@ -1223,6 +1287,13 @@ if(NOT ${TESTS_BAT}) ${TEST_CLIENT_ID_UTILITY} ) + set(TEST_CLIENT_ID_UTILITY_MASKED_DISCONTINUOUS_511_CONFIG_FILE ${TEST_CLIENT_ID_NAME}_utility_discontinuous_masked_511.json) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/client_id_tests/${TEST_CLIENT_ID_UTILITY_MASKED_DISCONTINUOUS_511_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_CLIENT_ID_UTILITY_MASKED_DISCONTINUOUS_511_CONFIG_FILE} + ${TEST_CLIENT_ID_UTILITY} + ) + # copy starter scripts into builddir set(TEST_CLIENT_ID_MASTER_STARTER ${TEST_CLIENT_ID_NAME}_master_starter.sh) copy_to_builddir(${PROJECT_SOURCE_DIR}/test/client_id_tests/${TEST_CLIENT_ID_MASTER_STARTER} @@ -2304,6 +2375,279 @@ if(NOT ${TESTS_BAT}) endif() ############################################################################## +# security_config_plugin_test +############################################################################## +if (${TEST_SECURITY}) + if(NOT ${TESTS_BAT}) + set(TEST_SECURITY_CONFIG_PLUGIN_NAME security_config_plugin_test) + set(TEST_SECURITY_CONFIG_PLUGIN_CLIENT ${TEST_SECURITY_CONFIG_PLUGIN_NAME}_client) + add_executable(${TEST_SECURITY_CONFIG_PLUGIN_CLIENT} + security_config_plugin_tests/${TEST_SECURITY_CONFIG_PLUGIN_CLIENT}.cpp + ) + + set(TEST_SECURITY_CONFIG_PLUGIN_SERVICE ${TEST_SECURITY_CONFIG_PLUGIN_NAME}_service) + add_executable(${TEST_SECURITY_CONFIG_PLUGIN_SERVICE} + security_config_plugin_tests/${TEST_SECURITY_CONFIG_PLUGIN_SERVICE}.cpp + ) + + message(STATUS "Using uid: ${TEST_UID}, gid: ${TEST_GID}") + + # Copy config file for vsomeipd, client and service into $BUILDDIR/test + set(TEST_SECURITY_CONFIG_PLUGIN_LOCAL_CONFIG_FILE ${TEST_SECURITY_CONFIG_PLUGIN_NAME}_local.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/security_config_plugin_tests/conf/${TEST_SECURITY_CONFIG_PLUGIN_LOCAL_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/security_config_plugin_tests/${TEST_SECURITY_CONFIG_PLUGIN_LOCAL_CONFIG_FILE} + @ONLY) + + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_config_plugin_tests/${TEST_SECURITY_CONFIG_PLUGIN_LOCAL_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_CONFIG_PLUGIN_LOCAL_CONFIG_FILE} + ${TEST_SECURITY_CONFIG_PLUGIN_CLIENT} + ${TEST_SECURITY_CONFIG_PLUGIN_SERVICE} + ) + + + # Copy bashscript to start vsomeipd and client into $BUILDDIR/test + set(TEST_SECURITY_CONFIG_PLUGIN_LOCAL_STARTER ${TEST_SECURITY_CONFIG_PLUGIN_NAME}_local_starter.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/security_config_plugin_tests/${TEST_SECURITY_CONFIG_PLUGIN_LOCAL_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_CONFIG_PLUGIN_LOCAL_STARTER} + ${TEST_SECURITY_CONFIG_PLUGIN_CLIENT} + ${TEST_SECURITY_CONFIG_PLUGIN_SERVICE} + ) + + target_link_libraries(${TEST_SECURITY_CONFIG_PLUGIN_CLIENT} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + target_link_libraries(${TEST_SECURITY_CONFIG_PLUGIN_SERVICE} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + endif() +endif() + +############################################################################# +# debug_diag_job_plugin_test +############################################################################## + +if(NOT ${TESTS_BAT}) + set(TEST_DEBUG_DIAG_JOB_NAME debug_diag_job_plugin_test) + + set(TEST_DEBUG_DIAG_JOB_SERVICE ${TEST_DEBUG_DIAG_JOB_NAME}_service) + add_executable(${TEST_DEBUG_DIAG_JOB_SERVICE} debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_SERVICE}.cpp) + target_link_libraries(${TEST_DEBUG_DIAG_JOB_SERVICE} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + # Copy config file for service into $BUILDDIR/test + set(TEST_DEBUG_DIAG_JOB_MASTER_CONFIG_FILE ${TEST_DEBUG_DIAG_JOB_NAME}_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/conf/${TEST_DEBUG_DIAG_JOB_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_DEBUG_DIAG_JOB_MASTER_CONFIG_FILE} + ${TEST_DEBUG_DIAG_JOB_SERVICE} + ) + + # Copy bashscript to start service into $BUILDDIR/test + set(TEST_DEBUG_DIAG_JOB_MASTER_STARTER ${TEST_DEBUG_DIAG_JOB_NAME}_master_starter.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_MASTER_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_DEBUG_DIAG_JOB_MASTER_STARTER} + ${TEST_DEBUG_DIAG_JOB_SERVICE} + ) + + set(TEST_DEBUG_DIAG_JOB_CLIENT ${TEST_DEBUG_DIAG_JOB_NAME}_client) + add_executable(${TEST_DEBUG_DIAG_JOB_CLIENT} + debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_CLIENT}.cpp + ) + target_link_libraries(${TEST_DEBUG_DIAG_JOB_CLIENT} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + # Copy config file for client into $BUILDDIR/test + set(TEST_DEBUG_DIAG_JOB_SLAVE_CONFIG_FILE ${TEST_DEBUG_DIAG_JOB_NAME}_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/conf/${TEST_DEBUG_DIAG_JOB_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_DEBUG_DIAG_JOB_SLAVE_CONFIG_FILE} + ${TEST_DEBUG_DIAG_JOB_CLIENT} + ) + + # Copy bashscript to start client into $BUILDDIR/test + set(TEST_DEBUG_DIAG_JOB_SLAVE_STARTER ${TEST_DEBUG_DIAG_JOB_NAME}_slave_starter.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/debug_diag_job_plugin_tests/${TEST_DEBUG_DIAG_JOB_SLAVE_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_DEBUG_DIAG_JOB_SLAVE_STARTER} + ${TEST_DEBUG_DIAG_JOB_CLIENT} + ) +endif() + +############################################################################## +# e2e test +############################################################################## + +if(NOT ${TESTS_BAT}) + set(TEST_E2E_NAME e2e_test) + set(TEST_E2E_SERVICE e2e_test_service) + set(TEST_E2E_CLIENT e2e_test_client) + + add_executable(${TEST_E2E_SERVICE} e2e_tests/${TEST_E2E_SERVICE}.cpp) + target_link_libraries(${TEST_E2E_SERVICE} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + add_executable(${TEST_E2E_CLIENT} + e2e_tests/${TEST_E2E_CLIENT}.cpp + ) + target_link_libraries(${TEST_E2E_CLIENT} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + # Copy service config file for external allow tests into $BUILDDIR/test + set(TEST_E2E_SERVICE_CONFIG_FILE_EXTERNAL ${TEST_E2E_NAME}_service_external.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/conf/${TEST_E2E_SERVICE_CONFIG_FILE_EXTERNAL}.in + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_SERVICE_CONFIG_FILE_EXTERNAL} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_SERVICE_CONFIG_FILE_EXTERNAL} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_SERVICE_CONFIG_FILE_EXTERNAL} + ${TEST_E2E_SERVICE} + ) + + # Copy client config file for external allow tests into $BUILDDIR/test + set(TEST_E2E_CLIENT_CONFIG_FILE_EXTERNAL ${TEST_E2E_NAME}_client_external.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/conf/${TEST_E2E_CLIENT_CONFIG_FILE_EXTERNAL}.in + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_CLIENT_CONFIG_FILE_EXTERNAL} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_CLIENT_CONFIG_FILE_EXTERNAL} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_CLIENT_CONFIG_FILE_EXTERNAL} + ${TEST_E2E_SERVICE} + ) + + # Copy bashscript to start external tests (master) into $BUILDDIR/test + set(TEST_E2E_EXTERNAL_MASTER_START_SCRIPT ${TEST_E2E_NAME}_external_master_start.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_EXTERNAL_MASTER_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_EXTERNAL_MASTER_START_SCRIPT} + ${TEST_E2E_SERVICE} + ) + + # Copy bashscript to start external tests (slave) into $BUILDDIR/test + set(TEST_E2E_EXTERNAL_SLAVE_START_SCRIPT ${TEST_E2E_NAME}_external_slave_start.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_EXTERNAL_SLAVE_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_EXTERNAL_SLAVE_START_SCRIPT} + ${TEST_E2E_SERVICE} + ) +endif() + +############################################################################## +# event tests +############################################################################## + +if(NOT ${TESTS_BAT}) + set(TEST_EVENT_NAME event_test) + set(TEST_EVENT_SERVICE ${TEST_EVENT_NAME}_service) + set(TEST_EVENT_CLIENT ${TEST_EVENT_NAME}_client) + + add_executable(${TEST_EVENT_SERVICE} event_tests/${TEST_EVENT_SERVICE}.cpp) + target_link_libraries(${TEST_EVENT_SERVICE} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + add_executable(${TEST_EVENT_CLIENT} + event_tests/${TEST_EVENT_CLIENT}.cpp + ) + target_link_libraries(${TEST_EVENT_CLIENT} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + # Copy service config file for external allow tests into $BUILDDIR/test + set(TEST_EVENT_SLAVE_TCP_CONFIG_FILE ${TEST_EVENT_NAME}_slave_tcp.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/event_tests/conf/${TEST_EVENT_SLAVE_TCP_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_SLAVE_TCP_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_SLAVE_TCP_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_SLAVE_TCP_CONFIG_FILE} + ${TEST_EVENT_SERVICE} + ) + + set(TEST_EVENT_SLAVE_UDP_CONFIG_FILE ${TEST_EVENT_NAME}_slave_udp.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/event_tests/conf/${TEST_EVENT_SLAVE_UDP_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_SLAVE_UDP_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_SLAVE_UDP_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_SLAVE_UDP_CONFIG_FILE} + ${TEST_EVENT_SERVICE} + ) + + set(TEST_EVENT_MASTER_CONFIG_FILE ${TEST_EVENT_NAME}_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/event_tests/conf/${TEST_EVENT_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_MASTER_CONFIG_FILE} + ${TEST_EVENT_CLIENT} + ) + + # Copy bashscript to start test (master) into $BUILDDIR/test + set(TEST_EVENT_MASTER_START_SCRIPT ${TEST_EVENT_NAME}_master_starter.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_MASTER_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_MASTER_START_SCRIPT} + ${TEST_EVENT_SERVICE} + ) + + # Copy bashscript to start external tests (slave) into $BUILDDIR/test + set(TEST_EVENT_SLAVE_START_SCRIPT ${TEST_EVENT_NAME}_slave_starter.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/event_tests/${TEST_EVENT_SLAVE_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_SLAVE_START_SCRIPT} + ${TEST_EVENT_CLIENT} + ) + +endif() + + +############################################################################## # Add for every test a dependency to gtest ############################################################################## @@ -2346,6 +2690,16 @@ if(NOT ${TESTS_BAT}) add_dependencies(${TEST_PENDING_SUBSCRIPTION_CLIENT} gtest) add_dependencies(${TEST_MALICIOUS_DATA_SERVICE} gtest) add_dependencies(${TEST_MALICIOUS_DATA_CLIENT} gtest) + if (${TEST_SECURITY}) + add_dependencies(${TEST_SECURITY_CONFIG_PLUGIN_CLIENT} gtest) + add_dependencies(${TEST_SECURITY_CONFIG_PLUGIN_SERVICE} gtest) + endif() + add_dependencies(${TEST_DEBUG_DIAG_JOB_SERVICE} gtest) + add_dependencies(${TEST_DEBUG_DIAG_JOB_CLIENT} gtest) + add_dependencies(${TEST_E2E_SERVICE} gtest) + add_dependencies(${TEST_E2E_CLIENT} gtest) + add_dependencies(${TEST_EVENT_SERVICE} gtest) + add_dependencies(${TEST_EVENT_CLIENT} gtest) else() add_dependencies(${TEST_LOCAL_ROUTING_SERVICE} gtest) add_dependencies(${TEST_LOCAL_ROUTING_CLIENT} gtest) @@ -2395,6 +2749,8 @@ if(NOT ${TESTS_BAT}) if (${TEST_SECURITY}) add_dependencies(build_tests ${TEST_SECURITY_SERVICE}) add_dependencies(build_tests ${TEST_SECURITY_CLIENT}) + add_dependencies(build_tests ${TEST_SECURITY_CONFIG_PLUGIN_CLIENT}) + add_dependencies(build_tests ${TEST_SECURITY_CONFIG_PLUGIN_SERVICE}) endif() add_dependencies(build_tests ${TEST_OFFERED_SERVICES_INFO_CLIENT}) add_dependencies(build_tests ${TEST_OFFERED_SERVICES_INFO_SERVICE}) @@ -2402,6 +2758,12 @@ if(NOT ${TESTS_BAT}) add_dependencies(build_tests ${TEST_PENDING_SUBSCRIPTION_CLIENT}) add_dependencies(build_tests ${TEST_MALICIOUS_DATA_SERVICE}) add_dependencies(build_tests ${TEST_MALICIOUS_DATA_CLIENT}) + add_dependencies(build_tests ${TEST_DEBUG_DIAG_JOB_SERVICE}) + add_dependencies(build_tests ${TEST_DEBUG_DIAG_JOB_CLIENT}) + add_dependencies(build_tests ${TEST_E2E_SERVICE}) + add_dependencies(build_tests ${TEST_E2E_CLIENT}) + add_dependencies(build_tests ${TEST_EVENT_SERVICE}) + add_dependencies(build_tests ${TEST_EVENT_CLIENT}) else() add_dependencies(build_tests ${TEST_LOCAL_ROUTING_SERVICE}) add_dependencies(build_tests ${TEST_LOCAL_ROUTING_CLIENT}) @@ -2462,7 +2824,7 @@ if(NOT ${TESTS_BAT}) add_test(NAME ${TEST_LOCAL_PAYLOAD_HUGE_NAME} COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_LOCAL_PAYLOAD_HUGE_STARTER} ) - set_tests_properties(${TEST_LOCAL_PAYLOAD_HUGE_NAME} PROPERTIES TIMEOUT 480) + set_tests_properties(${TEST_LOCAL_PAYLOAD_HUGE_NAME} PROPERTIES TIMEOUT 960) add_test(NAME ${TEST_EXTERNAL_LOCAL_PAYLOAD_CLIENT_LOCAL_NAME} COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_EXTERNAL_LOCAL_PAYLOAD_CLIENT_LOCAL_STARTER} ) @@ -2551,21 +2913,28 @@ if(NOT ${TESTS_BAT}) set_property(TEST ${TEST_CLIENT_ID_UTILITY}_masked_511 APPEND PROPERTY ENVIRONMENT "VSOMEIP_CONFIGURATION=${TEST_CLIENT_ID_UTILITY_MASKED_511_CONFIG_FILE}") - set_tests_properties(${TEST_CLIENT_ID_UTILITY} PROPERTIES TIMEOUT 120) + set_tests_properties(${TEST_CLIENT_ID_UTILITY}_masked_511 PROPERTIES TIMEOUT 120) add_test(NAME ${TEST_CLIENT_ID_UTILITY}_masked_4095 COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_CLIENT_ID_UTILITY}) set_property(TEST ${TEST_CLIENT_ID_UTILITY}_masked_4095 APPEND PROPERTY ENVIRONMENT "VSOMEIP_CONFIGURATION=${TEST_CLIENT_ID_UTILITY_MASKED_4095_CONFIG_FILE}") - set_tests_properties(${TEST_CLIENT_ID_UTILITY} PROPERTIES TIMEOUT 120) + set_tests_properties(${TEST_CLIENT_ID_UTILITY}_masked_4095 PROPERTIES TIMEOUT 600) add_test(NAME ${TEST_CLIENT_ID_UTILITY}_masked_127 COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_CLIENT_ID_UTILITY}) set_property(TEST ${TEST_CLIENT_ID_UTILITY}_masked_127 APPEND PROPERTY ENVIRONMENT "VSOMEIP_CONFIGURATION=${TEST_CLIENT_ID_UTILITY_MASKED_127_CONFIG_FILE}") - set_tests_properties(${TEST_CLIENT_ID_UTILITY} PROPERTIES TIMEOUT 120) + set_tests_properties(${TEST_CLIENT_ID_UTILITY}_masked_127 PROPERTIES TIMEOUT 120) + + add_test(NAME ${TEST_CLIENT_ID_UTILITY}_discontinuous_masked_511 + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_CLIENT_ID_UTILITY}) + set_property(TEST ${TEST_CLIENT_ID_UTILITY}_discontinuous_masked_511 + APPEND PROPERTY ENVIRONMENT + "VSOMEIP_CONFIGURATION=${TEST_CLIENT_ID_UTILITY_MASKED_DISCONTINUOUS_511_CONFIG_FILE}") + set_tests_properties(${TEST_CLIENT_ID_UTILITY}_discontinuous_masked_511 PROPERTIES TIMEOUT 120) # subscribe notify tests add_test(NAME ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_udp @@ -2835,8 +3204,19 @@ if(NOT ${TESTS_BAT}) if (${TEST_SECURITY}) # Security tests add_test(NAME ${TEST_SECURITY_NAME} - COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_SERVICE_START_SCRIPT} + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_SERVICE_LOCAL_START_SCRIPT} + ) + add_test(NAME ${TEST_SECURITY_NAME}_external_allow + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_EXTERNAL_MASTER_START_SCRIPT} security_test_config_client_external_allow.json --allow ) + add_test(NAME ${TEST_SECURITY_NAME}_external_deny + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_EXTERNAL_MASTER_START_SCRIPT} security_test_config_client_external_deny.json --deny + ) + + # security_config_plugin_test + add_test(NAME ${TEST_SECURITY_CONFIG_PLUGIN_NAME} + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SECURITY_CONFIG_PLUGIN_LOCAL_STARTER}) + set_tests_properties(${TEST_SECURITY_CONFIG_PLUGIN_NAME} PROPERTIES TIMEOUT 180) endif() # pending subscriptions test @@ -2868,10 +3248,57 @@ if(NOT ${TESTS_BAT}) COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_PENDING_SUBSCRIPTION_MASTER_STARTER} SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE) set_tests_properties(${TEST_PENDING_SUBSCRIPTION_NAME}_subscribe_stopsubscribe_subscribe PROPERTIES TIMEOUT 180) + add_test(NAME ${TEST_PENDING_SUBSCRIPTION_NAME}_send_request_to_sd_port + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_PENDING_SUBSCRIPTION_MASTER_STARTER} REQUEST_TO_SD) + set_tests_properties(${TEST_PENDING_SUBSCRIPTION_NAME}_send_request_to_sd_port PROPERTIES TIMEOUT 180) + # malicious data test - add_test(NAME ${TEST_MALICIOUS_DATA_NAME} - COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_MALICIOUS_DATA_MASTER_STARTER}) - set_tests_properties(${TEST_MALICIOUS_DATA_NAME} PROPERTIES TIMEOUT 180) + add_test(NAME ${TEST_MALICIOUS_DATA_NAME}_events + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_MALICIOUS_DATA_MASTER_STARTER} MALICIOUS_EVENTS) + set_tests_properties(${TEST_MALICIOUS_DATA_NAME}_events PROPERTIES TIMEOUT 180) + + add_test(NAME ${TEST_MALICIOUS_DATA_NAME}_protocol_version + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_MALICIOUS_DATA_MASTER_STARTER} PROTOCOL_VERSION) + set_tests_properties(${TEST_MALICIOUS_DATA_NAME}_protocol_version PROPERTIES TIMEOUT 180) + + add_test(NAME ${TEST_MALICIOUS_DATA_NAME}_message_type + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_MALICIOUS_DATA_MASTER_STARTER} MESSAGE_TYPE) + set_tests_properties(${TEST_MALICIOUS_DATA_NAME}_message_type PROPERTIES TIMEOUT 180) + + add_test(NAME ${TEST_MALICIOUS_DATA_NAME}_return_code + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_MALICIOUS_DATA_MASTER_STARTER} RETURN_CODE) + set_tests_properties(${TEST_MALICIOUS_DATA_NAME}_return_code PROPERTIES TIMEOUT 180) + + add_test(NAME ${TEST_MALICIOUS_DATA_NAME}_wrong_header_fields_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_MALICIOUS_DATA_MASTER_STARTER} WRONG_HEADER_FIELDS_UDP) + set_tests_properties(${TEST_MALICIOUS_DATA_NAME}_wrong_header_fields_udp PROPERTIES TIMEOUT 180) + + # debug_diag_job_plugin_test + add_test(NAME ${TEST_DEBUG_DIAG_JOB_NAME} + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_DEBUG_DIAG_JOB_MASTER_STARTER}) + set_tests_properties(${TEST_DEBUG_DIAG_JOB_NAME} PROPERTIES TIMEOUT 180) + + # e2e test + add_test(NAME ${TEST_E2E_NAME}_external + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_E2E_EXTERNAL_MASTER_START_SCRIPT} e2e_test_client_external.json) + set_tests_properties(${TEST_E2E_NAME}_external PROPERTIES TIMEOUT 180) + + # event tests + add_test(NAME ${TEST_EVENT_NAME}_payload_fixed_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_MASTER_START_SCRIPT} PAYLOAD_FIXED UDP) + set_tests_properties(${TEST_EVENT_NAME}_payload_fixed_udp PROPERTIES TIMEOUT 180) + + add_test(NAME ${TEST_EVENT_NAME}_payload_fixed_tcp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_MASTER_START_SCRIPT} PAYLOAD_FIXED TCP) + set_tests_properties(${TEST_EVENT_NAME}_payload_fixed_tcp PROPERTIES TIMEOUT 180) + + add_test(NAME ${TEST_EVENT_NAME}_payload_dynamic_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_MASTER_START_SCRIPT} PAYLOAD_DYNAMIC UDP) + set_tests_properties(${TEST_EVENT_NAME}_payload_dynamic_udp PROPERTIES TIMEOUT 180) + + add_test(NAME ${TEST_EVENT_NAME}_payload_dynamic_tcp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_MASTER_START_SCRIPT} PAYLOAD_DYNAMIC TCP) + set_tests_properties(${TEST_EVENT_NAME}_payload_dynamic_tcp PROPERTIES TIMEOUT 180) else() # Routing tests add_test(NAME ${TEST_LOCAL_ROUTING_NAME} diff --git a/test/client_id_tests/client_id_test_utility.cpp b/test/client_id_tests/client_id_test_utility.cpp index 1512146..e7f9770 100644 --- a/test/client_id_tests/client_id_test_utility.cpp +++ b/test/client_id_tests/client_id_test_utility.cpp @@ -51,6 +51,7 @@ public: } protected: virtual void SetUp() { + unlink(std::string("/dev/shm").append(utility::get_shm_name(configuration_)).c_str()); ASSERT_FALSE(file_exist(std::string("/dev/shm").append(utility::get_shm_name(configuration_)))); ASSERT_TRUE(static_cast<bool>(configuration_)); configuration_->load(APPLICATION_NAME_ROUTING_MANAGER); @@ -340,8 +341,11 @@ TEST_F(client_id_utility_test, TEST_F(client_id_utility_test, exhaust_client_id_range_sequential) { std::vector<client_t> its_client_ids; - - const std::uint16_t max_possible_clients = static_cast<std::uint16_t>(~diagnosis_mask_); + std::uint16_t its_max_clients(0); + for (int var = 0; var < __builtin_popcount(static_cast<std::uint16_t>(~diagnosis_mask_)); ++var) { + its_max_clients = static_cast<std::uint16_t>(its_max_clients | (1 << var)); + } + const std::uint16_t max_possible_clients = its_max_clients; // -1 for the routing manager, -2 as two predefined client IDs are present // in the json file which aren't assigned via autoconfiguration const std::uint16_t max_allowed_clients = static_cast<std::uint16_t>(max_possible_clients - 3u); @@ -352,6 +356,9 @@ TEST_F(client_id_utility_test, exhaust_client_id_range_sequential) { APPLICATION_NAME_NOT_PREDEFINED, 0x0); EXPECT_NE(ILLEGAL_CLIENT, its_client_id); if (its_client_id != ILLEGAL_CLIENT) { + if (i > 0) { + EXPECT_LT(its_client_ids.back(), its_client_id); + } its_client_ids.push_back(its_client_id); } else { ADD_FAILURE()<< "Received ILLEGAL_CLIENT " @@ -402,7 +409,11 @@ TEST_F(client_id_utility_test, exhaust_client_id_range_fragmented) { // -1 for the routing manager, -2 as two predefined client IDs are present // in the json file which aren't assigned via autoconfiguration - const std::uint16_t max_possible_clients = static_cast<std::uint16_t>(~diagnosis_mask_); + std::uint16_t its_max_clients(0); + for (int var = 0; var < __builtin_popcount(static_cast<std::uint16_t>(~diagnosis_mask_)); ++var) { + its_max_clients = static_cast<std::uint16_t>(its_max_clients | (1 << var)); + } + const std::uint16_t max_possible_clients = its_max_clients; const std::uint16_t max_allowed_clients = static_cast<std::uint16_t>(max_possible_clients - 3u); // acquire maximum amount of client IDs @@ -411,6 +422,9 @@ TEST_F(client_id_utility_test, exhaust_client_id_range_fragmented) { APPLICATION_NAME_NOT_PREDEFINED, 0x0); EXPECT_NE(ILLEGAL_CLIENT, its_client_id); if (its_client_id != ILLEGAL_CLIENT) { + if (i > 0) { + EXPECT_LT(its_client_ids.back(), its_client_id); + } its_client_ids.push_back(its_client_id); } else { ADD_FAILURE() << "Received ILLEGAL_CLIENT " @@ -447,6 +461,9 @@ TEST_F(client_id_utility_test, exhaust_client_id_range_fragmented) { APPLICATION_NAME_NOT_PREDEFINED, 0x0); EXPECT_NE(ILLEGAL_CLIENT, its_client_id); if (its_client_id != ILLEGAL_CLIENT) { + if (i > 0) { + EXPECT_LT(its_client_ids.back(), its_client_id); + } its_client_ids.push_back(its_client_id); } else { ADD_FAILURE() << "Received ILLEGAL_CLIENT " @@ -466,6 +483,193 @@ TEST_F(client_id_utility_test, exhaust_client_id_range_fragmented) { } } +/* + * @test Check that the autoconfigured client IDs continue to increase even if + * some client IDs at the beginning of the range are already released again + */ +TEST_F(client_id_utility_test, exhaust_client_id_range_fragmented_extended) { + std::vector<client_t> its_client_ids; + + // -1 for the routing manager, -2 as two predefined client IDs are present + // in the json file which aren't assigned via autoconfiguration + std::uint16_t its_max_clients(0); + for (int var = 0; var < __builtin_popcount(static_cast<std::uint16_t>(~diagnosis_mask_)); ++var) { + its_max_clients = static_cast<std::uint16_t>(its_max_clients | (1 << var)); + } + const std::uint16_t max_possible_clients = its_max_clients; + const std::uint16_t intermediate_release = 3; + const std::uint16_t max_allowed_clients = static_cast<std::uint16_t>(max_possible_clients - 3u); + + + // acquire (almost) maximum amount of client IDs + for (std::uint16_t i = 0; i < max_allowed_clients - intermediate_release; i++) { + client_t its_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_client_id); + if (its_client_id != ILLEGAL_CLIENT) { + if (i > 0) { + EXPECT_LT(its_client_ids.back(), its_client_id); + } + its_client_ids.push_back(its_client_id); + } else { + ADD_FAILURE() << "Received ILLEGAL_CLIENT " + << static_cast<std::uint32_t>(i); + } + } + + // release the first intermediate_release client IDs again + std::vector<client_t> its_intermediate_released_client_ids; + for (size_t i = 0; i < intermediate_release; i++ ) { + its_intermediate_released_client_ids.push_back(its_client_ids[i]); + utility::release_client_id(its_client_ids[i]); + its_client_ids.erase(its_client_ids.begin() + i); + } + + // acquire some more client IDs, these should be bigger than the already acquired + for (std::uint16_t i = 0; i < intermediate_release; i++) { + client_t its_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_client_id); + if (its_client_id != ILLEGAL_CLIENT) { + EXPECT_LT(its_client_ids.back(), its_client_id); + its_client_ids.push_back(its_client_id); + } else { + ADD_FAILURE() << "Received ILLEGAL_CLIENT " + << static_cast<std::uint32_t>(i); + } + } + + // check correct wrap around of client IDs + for (std::uint16_t i = 0; i < intermediate_release; i++) { + client_t its_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_client_id); + if (its_client_id != ILLEGAL_CLIENT) { + if (i == 0) { + EXPECT_GT(its_client_ids.back(), its_client_id); + } else { + EXPECT_LT(its_client_ids.back(), its_client_id); + } + its_client_ids.push_back(its_client_id); + } else { + ADD_FAILURE() << "Received ILLEGAL_CLIENT " + << static_cast<std::uint32_t>(i); + } + } + + // check limit is reached + client_t its_illegal_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_EQ(ILLEGAL_CLIENT, its_illegal_client_id); + + // release every second requested client ID + std::vector<client_t> its_released_client_ids; + for (size_t i = 0; i < its_client_ids.size(); i++ ) { + if (i % 2) { + its_released_client_ids.push_back(its_client_ids[i]); + utility::release_client_id(its_client_ids[i]); + } + } + for (const client_t c : its_released_client_ids) { + for (auto it = its_client_ids.begin(); it != its_client_ids.end(); ) { + if (*it == c) { + it = its_client_ids.erase(it); + } else { + ++it; + } + } + } + + // acquire client IDs up to the maximum allowed amount again + for (std::uint16_t i = 0; i < its_released_client_ids.size(); i++) { + client_t its_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_client_id); + if (its_client_id != ILLEGAL_CLIENT) { + if (i > 0) { + EXPECT_LT(its_client_ids.back(), its_client_id); + } + its_client_ids.push_back(its_client_id); + } else { + ADD_FAILURE() << "Received ILLEGAL_CLIENT " + << static_cast<std::uint32_t>(i); + } + } + + // check limit is reached + its_illegal_client_id = 0xFFFF; + its_illegal_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_EQ(ILLEGAL_CLIENT, its_illegal_client_id); + + // release all + for (const client_t c : its_client_ids) { + utility::release_client_id(c); + } +} + +TEST_F(client_id_utility_test, request_released_client_id_after_maximum_client_id_is_assigned) { + std::vector<client_t> its_client_ids; + std::uint16_t its_max_clients(0); + for (int var = 0; var < __builtin_popcount(static_cast<std::uint16_t>(~diagnosis_mask_)); ++var) { + its_max_clients = static_cast<std::uint16_t>(its_max_clients | (1 << var)); + } + const std::uint16_t max_possible_clients = its_max_clients; + // -1 for the routing manager, -2 as two predefined client IDs are present + // in the json file which aren't assigned via autoconfiguration + const std::uint16_t max_allowed_clients = static_cast<std::uint16_t>(max_possible_clients - 3u); + + // acquire (almost) maximum amount of client IDs + for (std::uint16_t i = 0; i < max_allowed_clients - 1; i++) { + client_t its_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_client_id); + if (its_client_id != ILLEGAL_CLIENT) { + if (i > 0) { + EXPECT_LT(its_client_ids.back(), its_client_id); + } + its_client_ids.push_back(its_client_id); + } else { + ADD_FAILURE()<< "Received ILLEGAL_CLIENT " + << static_cast<std::uint32_t>(i); + } + } + + // release a client ID + utility::release_client_id(its_client_ids[10]); + + // requesting an ID should return the maximum possible ID + client_t its_max_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_max_client_id); + its_client_ids.push_back(its_max_client_id); + + // requesting an ID should work as we have released one before + client_t its_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_client_id); + its_client_ids.push_back(its_client_id); + + // requesting an ID should not work as all IDs are in use now + client_t its_illegal_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_EQ(ILLEGAL_CLIENT, its_illegal_client_id); + + // release another ID + utility::release_client_id(its_client_ids[5]); + + its_client_id = utility::request_client_id(configuration_, + APPLICATION_NAME_NOT_PREDEFINED, 0x0); + EXPECT_NE(ILLEGAL_CLIENT, its_client_id); + its_client_ids.push_back(its_client_id); + + // release all + for (const client_t c : its_client_ids) { + utility::release_client_id(c); + } + its_client_ids.clear(); +} + #ifndef _WIN32 int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/test/client_id_tests/client_id_test_utility_discontinuous_masked_511.json b/test/client_id_tests/client_id_test_utility_discontinuous_masked_511.json new file mode 100644 index 0000000..4195bad --- /dev/null +++ b/test/client_id_tests/client_id_test_utility_discontinuous_masked_511.json @@ -0,0 +1,36 @@ +{ + "unicast":"127.0.0.1", + "logging": + { + "level":"warning", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "diagnosis":"0x63", + "diagnosis_mask":"0xFB00", + "applications": + [ + { + "name":"client_id_test_utility_service_in", + "id":"0x6311" + }, + { + "name":"client_id_test_utility_service_in_two", + "id":"0x6312" + }, + { + "name":"client_id_test_utility_service_out_low", + "id":"0x5911" + }, + { + "name":"client_id_test_utility_service_out_high", + "id":"0x7411" + } + ], + "routing":"vsomeipd" +} diff --git a/test/configuration_tests/configuration-test-deprecated.json b/test/configuration_tests/configuration-test-deprecated.json index 5ffacce..ecbe43b 100644 --- a/test/configuration_tests/configuration-test-deprecated.json +++ b/test/configuration_tests/configuration-test-deprecated.json @@ -21,7 +21,7 @@ "file-permissions" : { "permissions-shm" : "444", - "umask" : "222" + "permissions-uds" : "222" }, "supports_selective_broadcasts" : { @@ -40,6 +40,14 @@ { "name" : "testname2", "id" : "testid2" + }, + { + "name" : "testname3", + "id" : "testid3" + }, + { + "name" : "testname4", + "id" : "testid4" } ], "filters" : @@ -47,14 +55,12 @@ { "channel" : "testname", "services" : ["0x1111",2222], - "methods" : ["0x1111",2222], - "clients" : ["0x1111",2222] + "type" : "positive" }, { "channel" : "testname2", "services" : ["0x3333",4444], - "methods" : ["0x3333",4444], - "clients" : ["0x3333",4444] + "type" : "negative" } ] }, @@ -266,6 +272,8 @@ "reliable" : [ 40000, 40001 ] } ], + "tcp-restart-aborts-max" : "15", + "tcp-connect-time-max" : "10000", "max-payload-size-local" : "15000", "max-payload-size-reliable" : "17000", "buffer-shrink-threshold" : "11", @@ -311,6 +319,10 @@ { "service" : "0x1235", "instance" : "0x5678" + }, + { + "service" : "0x1236", + "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "5678"] } ] } @@ -323,7 +335,30 @@ [ { "service" : "0x1234", - "instance" : "0x5678" + "instances" : [ + { + "ids" : ["0x5678", { "first" : "0x5679", "last" : "0x5699"}], + "methods" : [ "0x0001", { "first" : "0x8001", "last" : "0x8006" }] + } + ] + }, + { + "service" : "0x1237", + "instances" : [ + { + "ids" : ["0x5678"], + "methods" : "any" + } + ] + }, + { + "service" : "0x1238", + "instances" : [ + { + "ids" : "any", + "methods" : ["0x0001"] + } + ] } ] } @@ -336,7 +371,12 @@ [ { "service" : "0x1234", - "instance" : "0x5678" + "instances" : [ + { + "ids" : ["0x5678", { "first" : "0x5679", "last" : "0x5699"}], + "methods" : [ "0x0002", { "first" : "0x9001", "last" : "0x9006" }] + } + ] } ], "offers": @@ -348,13 +388,87 @@ { "service" : "0x1235", "instance" : "0x5678" + }, + { + "service" : "0x1236", + "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "5678"] + } + ] + } + }, + { + "client" : "0x1550", + "deny" : + { + } + }, + { + "client" : "0x1660", + "allow" : + { + } + }, + { + "client" : "0x1770", + "deny" : + { + "requests": + [ + { + "service" : "0x1234", + "instances" : [ + { + "ids" : ["0x5678"], + "methods" : "any" + } + ] + } + ] + } + }, + { + "client" : "0x1880", + "allow" : + { + "requests": + [ + { + "service" : "0x1234", + "instances" : [ + { + "ids" : ["0x5678"], + "methods" : "any" + } + ] } ] } } ] }, + "security-update-whitelist" : + { + "uids" : + [ + {"first" : "1000", "last" : "1008"}, + {"first" : "1100", "last" : "1200"}, + "2000", + "3000" + ], + "services" : + [ + {"first" : "0x1234", "last" : "0x1238"}, + {"first" : "0x2000", "last" : "0x2500"}, + "0x7800" + ], + "check-whitelist" : "true" + }, "routing" : "my_application", + "routing-credentials" : + { + "uid" : "0x123", + "gid" : "0x456" + }, "service-discovery" : { "enable" : "true", diff --git a/test/configuration_tests/configuration-test.cpp b/test/configuration_tests/configuration-test.cpp index bdf66e0..c84af38 100644 --- a/test/configuration_tests/configuration-test.cpp +++ b/test/configuration_tests/configuration-test.cpp @@ -14,6 +14,7 @@ #include "../implementation/configuration/include/configuration.hpp" #include "../implementation/configuration/include/configuration_impl.hpp" #include "../implementation/logging/include/logger.hpp" +#include "../implementation/configuration/include/policy.hpp" #include "../implementation/plugin/include/plugin_manager.hpp" @@ -205,7 +206,7 @@ void check_file(const std::string &_config_file, // file permissions EXPECT_EQ(0444u, its_configuration->get_permissions_shm()); - EXPECT_EQ(0222u, its_configuration->get_umask()); + EXPECT_EQ(0222u, its_configuration->get_permissions_uds()); // selective broadcasts EXPECT_TRUE(its_configuration->supports_selective_broadcasts( @@ -215,43 +216,71 @@ void check_file(const std::string &_config_file, std::shared_ptr<vsomeip::cfg::trace> its_trace = its_configuration->get_trace(); EXPECT_TRUE(its_trace->is_enabled_); EXPECT_TRUE(its_trace->is_sd_enabled_); - EXPECT_EQ(2u, its_trace->channels_.size()); - EXPECT_EQ(2u, its_trace->filter_rules_.size()); + EXPECT_EQ(4u, its_trace->channels_.size()); + EXPECT_TRUE(its_trace->filters_.size() == 2u || its_trace->filters_.size() == 4u); for (const auto &c : its_trace->channels_) { - EXPECT_TRUE(c->name_ == std::string("testname") || c->name_ == std::string("testname2")); + EXPECT_TRUE(c->name_ == std::string("testname") || c->name_ == std::string("testname2") || + c->name_ == std::string("testname3") || c->name_ == std::string("testname4")); if (c->name_ == std::string("testname")) { EXPECT_EQ(std::string("testid"), c->id_); } else if (c->name_ == std::string("testname2")) { EXPECT_EQ(std::string("testid2"), c->id_); + } else if (c->name_ == std::string("testname3")) { + EXPECT_EQ(std::string("testid3"), c->id_); + } else if (c->name_ == std::string("testname4")) { + EXPECT_EQ(std::string("testid4"), c->id_); } } - for (const auto &f : its_trace->filter_rules_) { - EXPECT_TRUE(f->channel_ == std::string("testname") || f->channel_ == std::string("testname2")); - if (f->channel_ == std::string("testname")) { - EXPECT_EQ(2u, f->services_.size()); - EXPECT_EQ(2u, f->methods_.size()); - EXPECT_EQ(2u, f->clients_.size()); - for (const vsomeip::service_t s : f->services_) { - EXPECT_TRUE(s == vsomeip::service_t(0x1111) || s == vsomeip::service_t(2222)); + for (const auto &f : its_trace->filters_) { + auto its_channel_name = f->channels_.front(); + auto its_matches = f->matches_; + EXPECT_TRUE(its_channel_name == std::string("testname") || its_channel_name == std::string("testname2") || + its_channel_name == std::string("testname3") || its_channel_name == std::string("testname4")); + if (its_channel_name == std::string("testname")) { + EXPECT_EQ(2u, its_matches.size()); + + for (const vsomeip::trace::match_t m : its_matches) { + EXPECT_TRUE(std::get<0>(m) == vsomeip::service_t(0x1111) || + std::get<0>(m) == vsomeip::service_t(2222)); + EXPECT_TRUE(std::get<1>(m) == vsomeip::instance_t(0xffff)); + EXPECT_TRUE(std::get<2>(m) == vsomeip::method_t(0xffff)); + EXPECT_TRUE(f->is_positive_); + EXPECT_FALSE(f->is_range_); } - for (const vsomeip::method_t s : f->methods_) { - EXPECT_TRUE(s == vsomeip::method_t(0x1111) || s == vsomeip::method_t(2222)); + } else if (its_channel_name == std::string("testname2")) { + EXPECT_EQ(2u, its_matches.size()); + + for (const vsomeip::trace::match_t m : its_matches) { + EXPECT_TRUE(std::get<0>(m) == vsomeip::service_t(0x3333) || + std::get<0>(m) == vsomeip::service_t(4444)); + EXPECT_TRUE(std::get<1>(m) == vsomeip::instance_t(0xffff)); + EXPECT_TRUE(std::get<2>(m) == vsomeip::method_t(0xffff)); + EXPECT_FALSE(f->is_positive_); + EXPECT_FALSE(f->is_range_); } - for (const vsomeip::client_t s : f->clients_) { - EXPECT_TRUE(s == vsomeip::client_t(0x1111) || s == vsomeip::client_t(2222)); + } else if (its_channel_name == std::string("testname3")) { + EXPECT_EQ(2u, its_matches.size()); + + for (const vsomeip::trace::match_t m : its_matches) { + EXPECT_TRUE(std::get<0>(m) == vsomeip::service_t(0x1111) || + std::get<0>(m) == vsomeip::service_t(0x3333)); + EXPECT_TRUE(std::get<1>(m) == vsomeip::instance_t(0xffff)); + EXPECT_TRUE(std::get<2>(m) == vsomeip::method_t(0xffff) || + std::get<2>(m) == vsomeip::method_t(0x8888)); + EXPECT_FALSE(f->is_positive_); + EXPECT_FALSE(f->is_range_); } - } else if (f->channel_ == std::string("testname2")) { - EXPECT_EQ(2u, f->services_.size()); - EXPECT_EQ(2u, f->methods_.size()); - EXPECT_EQ(2u, f->clients_.size()); - for (const vsomeip::service_t s : f->services_) { - EXPECT_TRUE(s == vsomeip::service_t(0x3333) || s == vsomeip::service_t(4444)); - } - for (const vsomeip::method_t s : f->methods_) { - EXPECT_TRUE(s == vsomeip::method_t(0x3333) || s == vsomeip::method_t(4444)); - } - for (const vsomeip::client_t s : f->clients_) { - EXPECT_TRUE(s == vsomeip::client_t(0x3333) || s == vsomeip::client_t(4444)); + } else if (its_channel_name == std::string("testname4")) { + EXPECT_EQ(2u, its_matches.size()); + + for (const vsomeip::trace::match_t m : its_matches) { + EXPECT_TRUE(std::get<0>(m) == vsomeip::service_t(0x1111) || + std::get<0>(m) == vsomeip::service_t(0x3333)); + EXPECT_TRUE(std::get<1>(m) == vsomeip::instance_t(0x0001)); + EXPECT_TRUE(std::get<2>(m) == vsomeip::method_t(0xffff) || + std::get<2>(m) == vsomeip::method_t(0x8888)); + EXPECT_FALSE(f->is_positive_); + EXPECT_TRUE(f->is_range_); } } } @@ -471,8 +500,24 @@ void check_file(const std::string &_config_file, EXPECT_EQ(15001u + 16, its_configuration->get_max_message_size_reliable("10.10.10.11", 7778)); // security + EXPECT_TRUE(its_configuration->check_routing_credentials(0x7788, 0x123, 0x456)); + + // GID does not match + EXPECT_FALSE(its_configuration->check_routing_credentials(0x7788, 0x123, 0x222)); + + // UID does not match + EXPECT_FALSE(its_configuration->check_routing_credentials(0x7788, 0x333, 0x456)); + + // client is not the routing manager + EXPECT_TRUE(its_configuration->check_routing_credentials(0x7777, 0x888, 0x999)); + EXPECT_TRUE(its_configuration->is_security_enabled()); EXPECT_TRUE(its_configuration->is_offer_allowed(0x1277, 0x1234, 0x5678)); + EXPECT_TRUE(its_configuration->is_offer_allowed(0x1277, 0x1235, 0x5678)); + EXPECT_TRUE(its_configuration->is_offer_allowed(0x1277, 0x1236, 0x5678)); + EXPECT_TRUE(its_configuration->is_offer_allowed(0x1277, 0x1236, 0x5676)); + + EXPECT_FALSE(its_configuration->is_offer_allowed(0x1277, 0x1236, 0x5679)); EXPECT_FALSE(its_configuration->is_offer_allowed(0x1277, 0x1234, 0x5679)); EXPECT_FALSE(its_configuration->is_offer_allowed(0x1277, 0x1233, 0x5679)); EXPECT_FALSE(its_configuration->is_offer_allowed(0x1266, 0x1233, 0x5679)); @@ -482,25 +527,138 @@ void check_file(const std::string &_config_file, EXPECT_TRUE(its_configuration->is_offer_allowed(0x1443, 0x1234, 0x5679)); EXPECT_TRUE(its_configuration->is_offer_allowed(0x1443, 0x1300, 0x1)); EXPECT_TRUE(its_configuration->is_offer_allowed(0x1443, 0x1300, 0x2)); - - EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5678)); - EXPECT_TRUE(its_configuration->is_client_allowed(0x1346, 0x1234, 0x5678)); - EXPECT_FALSE(its_configuration->is_client_allowed(0x1347, 0x1234, 0x5678)); - EXPECT_FALSE(its_configuration->is_client_allowed(0x1342, 0x1234, 0x5678)); - EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5679)); - EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1230, 0x5678)); - // explicitly denied requests - EXPECT_FALSE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5678)); - EXPECT_FALSE(its_configuration->is_client_allowed(0x1446, 0x1234, 0x5678)); - EXPECT_TRUE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5679)); - EXPECT_TRUE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5679)); - EXPECT_FALSE(its_configuration->is_client_allowed(0x1442, 0x1234, 0x5678)); - EXPECT_FALSE(its_configuration->is_client_allowed(0x1447, 0x1234, 0x5678)); + EXPECT_FALSE(its_configuration->is_offer_allowed(0x1443, 0x1236, 0x5678)); + EXPECT_FALSE(its_configuration->is_offer_allowed(0x1443, 0x1236, 0x5675)); + EXPECT_FALSE(its_configuration->is_offer_allowed(0x1443, 0x1236, 0x5676)); + EXPECT_FALSE(its_configuration->is_offer_allowed(0x1443, 0x1236, 0x5677)); + EXPECT_TRUE(its_configuration->is_offer_allowed(0x1443, 0x1236, 0x5679)); + + // explicitly allowed requests of methods / events + EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5678, 0x0001)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5678, 0x8002)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1346, 0x1234, 0x5688, 0x8002)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5699, 0x8006)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5699, 0x8001)); + + EXPECT_FALSE(its_configuration->is_client_allowed(0x1347, 0x1234, 0x5678, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1342, 0x1234, 0x5678, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5677, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5700, 0x0001)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5699, 0x8007)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1234, 0x5700, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1230, 0x5678, 0x0001)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1230, 0x5678, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5678, 0x0002)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1446, 0x1234, 0x5678, 0xFFFF)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5679, 0x0003)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5679, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5699, 0x9001)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1443, 0x1234, 0x5699, 0x9006)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1442, 0x1234, 0x5678, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1447, 0x1234, 0x5678, 0xFFFF)); + + // check that any method ID is allowed + EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1237, 0x5678, 0x0001)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1237, 0x5678, 0xFFFF)); + + // check that any instance ID is allowed but only one method ID + EXPECT_TRUE(its_configuration->is_client_allowed(0x1343, 0x1238, 0x0004, 0x0001)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1343, 0x1238, 0x0004, 0x0002)); + + // DENY NOTHING policy + // check that ANY_METHOD is allowed in a "deny nothing" policy + EXPECT_TRUE(its_configuration->is_client_allowed(0x1550, 0x1234, 0x5678, 0xFFFF)); + // check that specific method ID is allowed in a "deny nothing" policy + EXPECT_TRUE(its_configuration->is_client_allowed(0x1550, 0x1234, 0x5678, 0x0001)); + + // ALLOW NOTHING policy + // check that ANY_METHOD is denied in a "allow nothing" policy + EXPECT_FALSE(its_configuration->is_client_allowed(0x1660, 0x1234, 0x5678, 0xFFFF)); + // check that specific method ID is denied in a "allow nothing" policy + EXPECT_FALSE(its_configuration->is_client_allowed(0x1660, 0x1234, 0x5678, 0x0001)); + + // DENY only one service instance and ANY_METHOD (0x01 - 0xFFFF) policy + EXPECT_FALSE(its_configuration->is_client_allowed(0x1770, 0x1234, 0x5678, 0xFFFF)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1770, 0x1234, 0x5678, 0x0001)); + + // allow only one service instance and ANY_METHOD policy + EXPECT_TRUE(its_configuration->is_client_allowed(0x1880, 0x1234, 0x5678, 0xFFFF)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1880, 0x1234, 0x5678, 0x0001)); + + // check request service + EXPECT_TRUE(its_configuration->is_client_allowed(0x1550, 0x1234, 0x5678, 0x00, true)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1660, 0x1234, 0x5678, 0x00, true)); + EXPECT_FALSE(its_configuration->is_client_allowed(0x1770, 0x1234, 0x5678, 0x00, true)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1770, 0x2222, 0x5678, 0x00, true)); + EXPECT_TRUE(its_configuration->is_client_allowed(0x1880, 0x1234, 0x5678, 0x00, true)); EXPECT_TRUE(its_configuration->check_credentials(0x1277, 1000, 1000)); EXPECT_FALSE(its_configuration->check_credentials(0x1277, 1001, 1001)); EXPECT_FALSE(its_configuration->check_credentials(0x1278, 1000, 1000)); + // Security update / removal whitelist + EXPECT_TRUE(its_configuration->is_policy_removal_allowed(1000)); + EXPECT_TRUE(its_configuration->is_policy_removal_allowed(1001)); + EXPECT_TRUE(its_configuration->is_policy_removal_allowed(1008)); + EXPECT_TRUE(its_configuration->is_policy_removal_allowed(2000)); + EXPECT_TRUE(its_configuration->is_policy_removal_allowed(3000)); + + EXPECT_FALSE(its_configuration->is_policy_removal_allowed(2001)); + EXPECT_FALSE(its_configuration->is_policy_removal_allowed(3001)); + + // create a valid policy object that is on whitelist and test is_policy_update_allowed method + std::shared_ptr<vsomeip::policy> _policy(std::make_shared<vsomeip::policy>()); + uint32_t its_uid = 1000; + uint32_t its_gid = 1000; + + // policy elements + std::pair<uint32_t, uint32_t> its_uid_range, its_gid_range; + std::set<std::pair<uint32_t, uint32_t>> its_uids, its_gids; + + // fill uid and gid range + std::get<0>(its_uid_range) = its_uid; + std::get<1>(its_uid_range) = its_uid; + std::get<0>(its_gid_range) = its_gid; + std::get<1>(its_gid_range) = its_gid; + its_uids.insert(its_uid_range); + its_gids.insert(its_gid_range); + + _policy->ids_.insert(std::make_pair(its_uids, its_gids)); + _policy->allow_who_ = true; + _policy->allow_what_ = true; + + uint16_t its_service_id = 0x1234; + vsomeip::ids_t its_instance_method_ranges; + vsomeip::ranges_t its_instance_ranges; + its_instance_ranges.insert(std::make_pair(0x01, 0x2)); + + vsomeip::ranges_t its_method_ranges; + its_method_ranges.insert(std::make_pair(0x01, 0x2)); + + _policy->services_.insert( + std::make_pair(its_service_id, its_instance_method_ranges)); + EXPECT_TRUE(its_configuration->is_policy_update_allowed(1000, _policy)); + + // test valid policy that holds a single service id which is whitelisted + its_service_id = 0x7800; + _policy->services_.insert( + std::make_pair(its_service_id, its_instance_method_ranges)); + EXPECT_TRUE(its_configuration->is_policy_update_allowed(1000, _policy)); + + // test invalid UID which is not whitelisted + EXPECT_FALSE(its_configuration->is_policy_update_allowed(2002, _policy)); + + // test invalid policy that additionally holds a service id which is not whitelisted + its_service_id = 0x8888; + _policy->services_.insert( + std::make_pair(its_service_id, its_instance_method_ranges)); + EXPECT_FALSE(its_configuration->is_policy_update_allowed(1000, _policy)); + + // TCP connection setting: + // max TCP connect time / max allowed number of aborted TCP endpoint restarts until forced restart + EXPECT_EQ(its_configuration->get_max_tcp_connect_time(), 10000u); + EXPECT_EQ(its_configuration->get_max_tcp_restart_aborts(), 15u); + // 6. Service discovery bool enabled = its_configuration->is_sd_enabled(); std::string protocol = its_configuration->get_sd_protocol(); diff --git a/test/configuration_tests/configuration-test.json b/test/configuration_tests/configuration-test.json index 32d11d7..d1ac335 100644 --- a/test/configuration_tests/configuration-test.json +++ b/test/configuration_tests/configuration-test.json @@ -21,7 +21,7 @@ "file-permissions" : { "permissions-shm" : "0444", - "umask" : "0222" + "permissions-uds" : "0222" }, "supports_selective_broadcasts" : { @@ -40,21 +40,51 @@ { "name" : "testname2", "id" : "testid2" + }, + { + "name" : "testname3", + "id" : "testid3" + }, + { + "name" : "testname4", + "id" : "testid4" } ], "filters" : [ { "channel" : "testname", - "services" : ["0x1111",2222], - "methods" : ["0x1111",2222], - "clients" : ["0x1111",2222] + "matches" : [ "0x1111", 2222 ], + "type" : "positive" }, { "channel" : "testname2", - "services" : ["0x3333",4444], - "methods" : ["0x3333",4444], - "clients" : ["0x3333",4444] + "matches" : [ "0x3333", 4444 ], + "type" : "negative" + }, + { + "channel" : "testname3", + "matches" : [ "0x1111", { "service" : "0x3333", "instance" : "0xffff", "method" : "0x8888" } ], + "type" : "negative" + }, + { + "channel" : "testname4", + "matches" : + { + "from" : + { + "service" : "0x1111", + "instance" : "0x0001", + "method" : "0xffff" + }, + "to" : + { + "service" : "0x3333", + "instance" : "0x0001", + "method" : "0x8888" + } + }, + "type" : "negative" } ] }, @@ -229,6 +259,8 @@ "reliable" : [ 40000, 40001 ] } ], + "tcp-restart-aborts-max" : "15", + "tcp-connect-time-max" : "10000", "max-payload-size-local" : "15000", "max-payload-size-reliable" : "17000", "buffer-shrink-threshold" : "11", @@ -274,6 +306,10 @@ { "service" : "0x1235", "instance" : "0x5678" + }, + { + "service" : "0x1236", + "instances" : [{ "first" : "0x5676", "last" : "0x5677"}, "5678"] } ] } @@ -286,7 +322,30 @@ [ { "service" : "0x1234", - "instance" : "0x5678" + "instances" : [ + { + "ids" : ["0x5678", { "first" : "0x5679", "last" : "0x5699"}], + "methods" : [ "0x0001", { "first" : "0x8001", "last" : "0x8006" }] + } + ] + }, + { + "service" : "0x1237", + "instances" : [ + { + "ids" : ["0x5678"], + "methods" : "any" + } + ] + }, + { + "service" : "0x1238", + "instances" : [ + { + "ids" : "any", + "methods" : ["0x0001"] + } + ] } ] } @@ -299,7 +358,12 @@ [ { "service" : "0x1234", - "instance" : "0x5678" + "instances" : [ + { + "ids" : ["0x5678", { "first" : "0x5679", "last" : "0x5699"}], + "methods" : [ "0x0002", { "first" : "0x9001", "last" : "0x9006" }] + } + ] } ], "offers": @@ -311,13 +375,87 @@ { "service" : "0x1235", "instance" : "0x5678" + }, + { + "service" : "0x1236", + "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "5678"] + } + ] + } + }, + { + "client" : "0x1550", + "deny" : + { + } + }, + { + "client" : "0x1660", + "allow" : + { + } + }, + { + "client" : "0x1770", + "deny" : + { + "requests": + [ + { + "service" : "0x1234", + "instances" : [ + { + "ids" : ["0x5678"], + "methods" : "any" + } + ] + } + ] + } + }, + { + "client" : "0x1880", + "allow" : + { + "requests": + [ + { + "service" : "0x1234", + "instances" : [ + { + "ids" : ["0x5678"], + "methods" : "any" + } + ] } ] } } ] }, + "security-update-whitelist" : + { + "uids" : + [ + {"first" : "1000", "last" : "1008"}, + {"first" : "1100", "last" : "1200"}, + "2000", + "3000" + ], + "services" : + [ + {"first" : "0x1234", "last" : "0x1238"}, + {"first" : "0x2000", "last" : "0x2500"}, + "0x7800" + ], + "check-whitelist" : "true" + }, "routing" : "my_application", + "routing-credentials" : + { + "uid" : "0x123", + "gid" : "0x456" + }, "service-discovery" : { "enable" : "true", @@ -333,4 +471,4 @@ "request_response_delay" : "1111", "offer_debounce_time" : "1000" } -}
\ No newline at end of file +} diff --git a/test/cpu_load_tests/cpu_load_test_service.cpp b/test/cpu_load_tests/cpu_load_test_service.cpp index b0b6ea6..6d21752 100644 --- a/test/cpu_load_tests/cpu_load_test_service.cpp +++ b/test/cpu_load_tests/cpu_load_test_service.cpp @@ -101,6 +101,7 @@ public: if(!is_registered_) { is_registered_ = true; + std::lock_guard<std::mutex> its_lock(mutex_); blocked_ = true; // "start" the run method thread condition_.notify_one(); diff --git a/test/debug_diag_job_plugin_tests/conf/debug_diag_job_plugin_test_master.json.in b/test/debug_diag_job_plugin_tests/conf/debug_diag_job_plugin_test_master.json.in new file mode 100644 index 0000000..cb47d1e --- /dev/null +++ b/test/debug_diag_job_plugin_tests/conf/debug_diag_job_plugin_test_master.json.in @@ -0,0 +1,67 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"warning", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "diagnosis" : "0x63", + "applications": + [ + { + "name":"debug_diag_job_plugin_test_service_one", + "id":"0x1010" + }, + { + "name":"debug_diag_job_plugin_test_service_two", + "id":"0x2222" + }, + { + "name":"vsomeipd", + "plugins" : + [ + { + "application_plugin" : "vsomeip-debug-diagnosis-plugin-mgu" + } + ] + } + ], + "services": + [ + { + "service":"0x1010", + "instance":"0x0001", + "unreliable":"40001" + }, + { + "service":"0x2020", + "instance":"0x0001", + "unreliable":"40002" + }, + { + "service":"0xfea3", + "instance":"0x80", + "unreliable":"50000" + } + ], + "routing":"vsomeipd", + "service-discovery": + { + "enable":"true", + "multicast":"224.1.1.1", + "port":"30490", + "protocol":"udp", + "initial_delay_min" : "10", + "initial_delay_max" : "10", + "repetitions_base_delay" : "30", + "repetitions_max" : "3", + "cyclic_offer_delay" : "1000", + "ttl" : "3" + } +}
\ No newline at end of file diff --git a/test/debug_diag_job_plugin_tests/conf/debug_diag_job_plugin_test_slave.json.in b/test/debug_diag_job_plugin_tests/conf/debug_diag_job_plugin_test_slave.json.in new file mode 100644 index 0000000..e8e38cf --- /dev/null +++ b/test/debug_diag_job_plugin_tests/conf/debug_diag_job_plugin_test_slave.json.in @@ -0,0 +1,35 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "logging": + { + "level":"warning", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"debug_diag_job_plugin_test_client", + "id":"0x9090" + } + ], + "routing":"vsomeipd", + "service-discovery": + { + "enable":"true", + "multicast":"224.1.1.1", + "port":"30490", + "protocol":"udp", + "initial_delay_min" : "10", + "initial_delay_max" : "10", + "repetitions_base_delay" : "30", + "repetitions_max" : "3", + "cyclic_offer_delay" : "1000", + "ttl" : "3" + } +}
\ No newline at end of file diff --git a/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_client.cpp b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_client.cpp new file mode 100644 index 0000000..a3e6e5e --- /dev/null +++ b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_client.cpp @@ -0,0 +1,748 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> +#include <cstring> + +#include <gtest/gtest.h> + +#ifndef _WIN32 +#include <signal.h> +#endif + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "../debug_diag_job_plugin_tests/debug_diag_job_plugin_test_globals.hpp" +class debug_diag_job_plugin_test_client; +static debug_diag_job_plugin_test_client* the_client; +extern "C" void signal_handler(int _signum); + + +enum class debug_diag_job_app_error_type_e : std::uint32_t { + ET_UNKNOWN = 0, + ET_OK = 256, + ET_OUT_OF_RANGE = 257, + ET_E_COMMUNICATION_ERROR = 576, +}; + +class debug_diag_job_plugin_test_client { +public: + debug_diag_job_plugin_test_client( + std::array<debug_diag_job_plugin_test::service_info, 3> _service_infos_local, + std::array<debug_diag_job_plugin_test::service_info, 3> _service_infos_remote) : + service_infos_local_(_service_infos_local), + service_infos_remote_(_service_infos_remote), + app_(vsomeip::runtime::get()->create_application()), + wait_until_registered_(true), + wait_until_remote_services_available_(true), + wait_until_debug_diag_job_service_available_(true), + wait_until_local_services_available_(true), + wait_until_local_services_unavailable_(true), + wait_until_notifications_received_(true), + wait_until_responses_received_(true), + wait_until_debug_diag_job_response_received_(true), + wait_for_stop_(true), + last_response_(debug_diag_job_app_error_type_e::ET_UNKNOWN), + stop_thread_(std::bind(&debug_diag_job_plugin_test_client::wait_for_stop, this)), + run_thread_(std::bind(&debug_diag_job_plugin_test_client::run, this)) { + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return; + } + + // register signal handler + the_client = this; + struct sigaction sa_new, sa_old; + sa_new.sa_handler = signal_handler; + sa_new.sa_flags = 0; + sigemptyset(&sa_new.sa_mask); + ::sigaction(SIGUSR1, &sa_new, &sa_old); + ::sigaction(SIGINT, &sa_new, &sa_old); + ::sigaction(SIGTERM, &sa_new, &sa_old); + ::sigaction(SIGABRT, &sa_new, &sa_old); + + app_->register_state_handler( + std::bind(&debug_diag_job_plugin_test_client::on_state, this, + std::placeholders::_1)); + + app_->register_message_handler(vsomeip::ANY_SERVICE, + vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, + std::bind(&debug_diag_job_plugin_test_client::on_message, this, + std::placeholders::_1)); + + for (const auto& serviceinfos : {service_infos_remote_, service_infos_local_}) { + for (const auto& serviceinfo : serviceinfos) { + if (serviceinfo.service_id == 0xFFFF && serviceinfo.instance_id == 0xFFFF) { + continue; + } + service_infos_.push_back(serviceinfo); + } + } + + // register availability for all other services and request their event. + for (const auto& i : service_infos_) { + app_->register_availability_handler(i.service_id, i.instance_id, + std::bind(&debug_diag_job_plugin_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service(i.service_id, i.instance_id); + + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(i.eventgroup_id); + app_->request_event(i.service_id, i.instance_id, + static_cast<vsomeip::event_t>(i.event_id), + its_eventgroups, true); + + other_services_available_[std::make_pair(i.service_id, i.instance_id)] = false; + + app_->subscribe(i.service_id, i.instance_id, i.eventgroup_id); + other_services_received_notification_[std::make_pair(i.service_id, i.event_id)] = 0; + other_services_received_response_[std::make_pair(i.service_id, i.instance_id)] = 0; + } + + for (const auto& si : service_infos_local_) { + if (si.service_id == 0xFFFF && si.instance_id == 0xFFFF) { + continue; + } + other_local_services_available_[std::make_pair(si.service_id, si.instance_id)] = false; + other_local_services_received_notification_[std::make_pair(si.service_id, si.event_id)] = 0; + } + + for (const auto& si : service_infos_remote_) { + if (si.service_id == 0xFFFF && si.instance_id == 0xFFFF) { + continue; + } + other_remote_services_available_[std::make_pair(si.service_id, si.instance_id)] = false; + other_remote_services_received_notification_[std::make_pair(si.service_id, si.event_id)] = 0; + } + app_->register_availability_handler( + debug_diag_job_plugin_test::debug_diag_job_serviceinfo.service_id, + debug_diag_job_plugin_test::debug_diag_job_serviceinfo.instance_id, + std::bind(&debug_diag_job_plugin_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service( + debug_diag_job_plugin_test::debug_diag_job_serviceinfo.service_id, + debug_diag_job_plugin_test::debug_diag_job_serviceinfo.instance_id, + 0x1, vsomeip::ANY_MINOR); + + app_->start(); + } + + ~debug_diag_job_plugin_test_client() { + run_thread_.join(); + stop_thread_.join(); + + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + if(_is_available) { + for (auto available_map : {&other_services_available_, + &other_local_services_available_, + &other_remote_services_available_}) { + auto its_service = available_map->find(std::make_pair(_service, _instance)); + if (its_service != available_map->end()) { + if (its_service->second != _is_available) { + its_service->second = true; + VSOMEIP_DEBUG << "Service [" + << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance + << "] is available."; + } + } + } + + if (std::all_of(other_remote_services_available_.cbegin(), + other_remote_services_available_.cend(), + [](const std::map<std::pair<vsomeip::service_t, + vsomeip::instance_t>, bool>::value_type& v) { + return v.second;})) { + VSOMEIP_INFO << " all remote services are available."; + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_remote_services_available_ = false; + condition_.notify_one(); + } + + if (_service == debug_diag_job_plugin_test::debug_diag_job_serviceinfo.service_id && + _instance == debug_diag_job_plugin_test::debug_diag_job_serviceinfo.instance_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_debug_diag_job_service_available_ = false; + condition_.notify_one(); + } + + if (std::all_of(other_local_services_available_.cbegin(), + other_local_services_available_.cend(), + [](const std::map<std::pair<vsomeip::service_t, + vsomeip::instance_t>, bool>::value_type& v) { + return v.second;})) { + VSOMEIP_INFO << "all local services are available."; + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_local_services_available_ = false; + condition_.notify_one(); + } + + if (std::all_of(other_services_available_.cbegin(), + other_services_available_.cend(), + [](const std::map<std::pair<vsomeip::service_t, + vsomeip::instance_t>, bool>::value_type& v) { + return v.second;})) { + VSOMEIP_INFO << "all local and remote services are available."; + } + } else { + bool was_available_before(false); + auto its_service = other_local_services_available_.find(std::make_pair(_service, _instance)); + if (its_service != other_local_services_available_.end()) { + if (its_service->second) { + its_service->second = false; + VSOMEIP_DEBUG << "Service [" + << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance + << "] is not available anymore."; + was_available_before = true; + } + } + if (was_available_before) { + if (std::all_of(other_local_services_available_.cbegin(), + other_local_services_available_.cend(), + [](const std::map<std::pair<vsomeip::service_t, + vsomeip::instance_t>, bool>::value_type& v) { + return !v.second;})) { + VSOMEIP_INFO << "all local services are not available anymore."; + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_local_services_unavailable_ = false; + condition_.notify_one(); + } + } + } + } + + void on_message(const std::shared_ptr<vsomeip::message> &_message) { + if (_message->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) { + + other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())]++; + + VSOMEIP_DEBUG + << "Received a notification with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method() <<"] (now have: " + << std::dec << other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())] << ")"; + + std::shared_ptr<vsomeip::payload> its_payload(_message->get_payload()); + EXPECT_EQ(2u, its_payload->get_length()); + EXPECT_EQ((_message->get_service() & 0xFF00 ) >> 8, its_payload->get_data()[0]); + EXPECT_EQ((_message->get_service() & 0xFF), its_payload->get_data()[1]); + bool notify = all_notifications_received(); + + if(notify) { + for (auto &os : other_local_services_received_notification_) { + os.second = 0; + } + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_notifications_received_ = false; + condition_.notify_one(); + } + } else if (_message->get_message_type() == vsomeip::message_type_e::MT_RESPONSE && + _message->get_service() != debug_diag_job_plugin_test::debug_diag_job_serviceinfo.service_id) { + other_services_received_response_[std::make_pair(_message->get_service(), + _message->get_instance())]++; + VSOMEIP_INFO + << "Received a response with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method() <<"] (now have: " + << std::dec << other_services_received_response_[std::make_pair(_message->get_service(), + _message->get_instance())] << ")"; + if (std::all_of(other_services_received_response_.begin(), + other_services_received_response_.end(), + [&](const std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, std::uint32_t>::value_type& v) { + return v.second > 0; + })) { + VSOMEIP_INFO << "received responses of all services!"; + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_responses_received_ = false; + condition_.notify_one(); + } + } else if (_message->get_message_type() == vsomeip::message_type_e::MT_RESPONSE && + _message->get_service() == debug_diag_job_plugin_test::debug_diag_job_serviceinfo.service_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_debug_diag_job_response_received_ = false; + last_response_ = debug_diag_job_app_error_type_e::ET_UNKNOWN; + auto its_payload = _message->get_payload(); + if (its_payload && its_payload->get_length() > 3) { + + last_response_ = static_cast<debug_diag_job_app_error_type_e>( + (its_payload->get_data()[0] << 24) | + (its_payload->get_data()[1] << 16) | + (its_payload->get_data()[2] << 8) | + (its_payload->get_data()[3])); + } + condition_.notify_one(); + } + } + + bool all_notifications_received() { + return std::all_of( + other_services_received_notification_.cbegin(), + other_services_received_notification_.cend(), + [&](const std::map<std::pair<vsomeip::service_t, + vsomeip::method_t>, std::uint32_t>::value_type& v) + { + if (v.second == debug_diag_job_plugin_test::notifications_to_send) { + return true; + } else { + if (v.second >= debug_diag_job_plugin_test::notifications_to_send) { + VSOMEIP_WARNING + << " Received multiple initial events from service/instance: " + << std::setw(4) << std::setfill('0') << std::hex << v.first.first + << "." + << std::setw(4) << std::setfill('0') << std::hex << v.first.second + << " number of received events: " << v.second + << ". This is caused by StopSubscribe/Subscribe messages and/or" + << " service offered via UDP and TCP"; + return false; + } else { + return false; + } + } + } + ); + } + + void handle_signal(int _signum) { + VSOMEIP_DEBUG << "Catched signal, going down (" + << std::dec <<_signum << ")"; + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + stop_condition_.notify_one(); + } + + void wait_for_stop() { + { + std::unique_lock<std::mutex> its_lock(stop_mutex_); + while (wait_for_stop_) { + stop_condition_.wait(its_lock); + } + VSOMEIP_INFO << "going down"; + } + for (const auto& i : service_infos_) { + if (i.service_id == 0xFFFF && i.instance_id == 0xFFFF) { + continue; + } + app_->unsubscribe(i.service_id, i.instance_id, i.eventgroup_id); + app_->release_event(i.service_id, i.instance_id, i.event_id); + app_->release_service(i.service_id, i.instance_id); + } + app_->clear_all_handler(); + app_->stop(); + } + + void call_debug_diag_job(bool _offer) { + std::shared_ptr<vsomeip::message> its_request = vsomeip::runtime::get()->create_request(); + its_request->set_service(debug_diag_job_plugin_test::debug_diag_job_serviceinfo.service_id); + its_request->set_instance(debug_diag_job_plugin_test::debug_diag_job_serviceinfo.instance_id); + if (_offer) { + its_request->set_method(debug_diag_job_plugin_test::debug_diag_job_serviceinfo.method_id); + std::vector<vsomeip::byte_t> its_payload; + for (int i =0; i<8; i++) { // handletype and size placeholder + its_payload.push_back(0x0); + } + // insert valid entries + for (const auto& si : service_infos_local_) { + if (si.service_id == 0xFFFF && si.instance_id == 0xFFFF) { + continue; + } + for (auto reliable : {false, true}) { // ensure to offer service reliable and unreliable + // service + its_payload.push_back(static_cast<vsomeip::byte_t>(si.service_id >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(si.service_id & 0xFF)); + // instance + its_payload.push_back(static_cast<vsomeip::byte_t>(si.instance_id >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(si.instance_id & 0xFF)); + // port (use event id as port) + its_payload.push_back(static_cast<vsomeip::byte_t>(si.event_id >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(si.event_id & 0xFF)); + // reliable + its_payload.push_back(reliable); + // magic_cookies_enabled + its_payload.push_back(reliable); + } + } + + std::size_t its_size = its_payload.size() - 8; + its_payload[4] = vsomeip::byte_t(its_size >> 24 & 0xFF); + its_payload[5] = vsomeip::byte_t(its_size >> 16 & 0xFF); + its_payload[6] = vsomeip::byte_t(its_size >> 8 & 0xFF); + its_payload[7] = vsomeip::byte_t(its_size & 0xFF); + + its_request->set_payload(vsomeip::runtime::get()->create_payload(its_payload)); + } else { + its_request->set_method(debug_diag_job_plugin_test::debug_diag_job_serviceinfo_reset.method_id); + } + std::unique_lock<std::mutex> its_lock(mutex_); + app_->send(its_request); + while(wait_until_debug_diag_job_response_received_) { + condition_.wait(its_lock); + } + EXPECT_EQ(debug_diag_job_app_error_type_e::ET_OK, last_response_); + wait_until_debug_diag_job_response_received_ = true; + + } + + void call_debug_diag_job_wrong_message(bool _offer) { + std::shared_ptr<vsomeip::message> its_request = vsomeip::runtime::get()->create_request(); + its_request->set_service(debug_diag_job_plugin_test::debug_diag_job_serviceinfo.service_id); + its_request->set_instance(debug_diag_job_plugin_test::debug_diag_job_serviceinfo.instance_id); + if (_offer) { + its_request->set_method(debug_diag_job_plugin_test::debug_diag_job_serviceinfo.method_id); + } else { + its_request->set_method(debug_diag_job_plugin_test::debug_diag_job_serviceinfo_reset.method_id); + } + std::vector<vsomeip::byte_t> its_payload; + for (int i =0; i<8; i++) { // handletype and size placeholder + its_payload.push_back(0x0); + } + // insert valid entries + for (const auto& si : service_infos_local_) { + if (si.service_id == 0xFFFF && si.instance_id == 0xFFFF) { + continue; + } + for (auto reliable : {false, true}) { // ensure to offer service reliable and unreliable + // service + its_payload.push_back(static_cast<vsomeip::byte_t>(si.service_id >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(si.service_id & 0xFF)); + // instance + its_payload.push_back(static_cast<vsomeip::byte_t>(si.instance_id >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(si.instance_id & 0xFF)); + // port (use event id as port) + its_payload.push_back(static_cast<vsomeip::byte_t>(si.event_id >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(si.event_id & 0xFF)); + // reliable + its_payload.push_back(reliable); + // magic_cookies_enabled + its_payload.push_back(reliable); + } + } + + // insert invalid entry + // service + its_payload.push_back(static_cast<vsomeip::byte_t>(0xAAAA >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(0xAAAA & 0xFF)); + // instance + its_payload.push_back(static_cast<vsomeip::byte_t>(0xBBBB >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(0xBBBB & 0xFF)); + // port + its_payload.push_back(static_cast<vsomeip::byte_t>(0xCCCC >> 8)); + its_payload.push_back(static_cast<vsomeip::byte_t>(0xCCCC & 0xFF)); + // reliable + its_payload.push_back(0x1); + // magic_cookies_enabled + its_payload.push_back(0x1); + + for (int var = 0; var < 12; ++var) { // insert invalid data covered by size + its_payload.push_back(0xdd); + } + + std::size_t its_size = its_payload.size() - 8; + its_payload[4] = vsomeip::byte_t(its_size >> 24 & 0xFF); + its_payload[5] = vsomeip::byte_t(its_size >> 16 & 0xFF); + its_payload[6] = vsomeip::byte_t(its_size >> 8 & 0xFF); + its_payload[7] = vsomeip::byte_t(its_size & 0xFF); + its_request->set_payload(vsomeip::runtime::get()->create_payload(its_payload)); + + auto send_request_and_wait_for_reply = [&]( + const std::shared_ptr<vsomeip::message>& _request, + debug_diag_job_app_error_type_e _expected_response) { + std::unique_lock<std::mutex> its_lock(mutex_); + app_->send(_request); + while(wait_until_debug_diag_job_response_received_) { + condition_.wait(its_lock); + } + EXPECT_EQ(_expected_response, last_response_); + wait_until_debug_diag_job_response_received_ = true; + }; + + send_request_and_wait_for_reply(its_request, debug_diag_job_app_error_type_e::ET_OUT_OF_RANGE); + + // send a request w/o payload + its_payload.clear(); + its_request->set_payload(vsomeip::runtime::get()->create_payload(its_payload)); + send_request_and_wait_for_reply(its_request, debug_diag_job_app_error_type_e::ET_OUT_OF_RANGE); + + + + // send a request with to few data and a too big size field + its_payload.clear(); + its_payload.push_back(0x0); // handle + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x11); // size + its_payload.push_back(0x22); + its_payload.push_back(0x33); + its_payload.push_back(0x44); + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_request->set_payload(vsomeip::runtime::get()->create_payload(its_payload)); + send_request_and_wait_for_reply(its_request, debug_diag_job_app_error_type_e::ET_OUT_OF_RANGE); + + + // send a request with to few data and a size of zero field + its_payload.clear(); + its_payload.push_back(0x0); // handle + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x0); // size + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_request->set_payload(vsomeip::runtime::get()->create_payload(its_payload)); + send_request_and_wait_for_reply(its_request, debug_diag_job_app_error_type_e::ET_OUT_OF_RANGE); + + // send a request with valid size and data but additional data at the end + its_payload.clear(); + its_payload.push_back(0x0); // handle + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x0); // size + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0x0); + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + its_payload.push_back(0xDD); // data + + its_size = its_payload.size() - 8; + its_payload[4] = vsomeip::byte_t(its_size >> 24 & 0xFF); + its_payload[5] = vsomeip::byte_t(its_size >> 16 & 0xFF); + its_payload[6] = vsomeip::byte_t(its_size >> 8 & 0xFF); + its_payload[7] = vsomeip::byte_t(its_size & 0xFF); + + its_payload.push_back(0xDD); // data + its_request->set_payload(vsomeip::runtime::get()->create_payload(its_payload)); + send_request_and_wait_for_reply(its_request, debug_diag_job_app_error_type_e::ET_OUT_OF_RANGE); + } + + void call_shutdown_method() { + std::shared_ptr<vsomeip::message> its_request = vsomeip::runtime::get()->create_request(); + + auto send_request = [&](const debug_diag_job_plugin_test::service_info& _info) { + if (_info.service_id == 0xFFFF && _info.instance_id == 0xFFFF) { + return; + } + its_request->set_service(_info.service_id); + its_request->set_instance(_info.instance_id); + its_request->set_method(_info.method_id); + app_->send(its_request); + }; + // insert valid entries + for (const auto& si : service_infos_local_) { + send_request(si); + } + for (const auto& si : service_infos_remote_) { + send_request(si); + } + } + + void run() { + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + } + // wait until remote services are available + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_remote_services_available_) { + condition_.wait(its_lock); + } + wait_until_remote_services_available_ = true; + VSOMEIP_WARNING << "Remote services available"; + } + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_debug_diag_job_service_available_) { + condition_.wait(its_lock); + } + wait_until_debug_diag_job_service_available_ = true; + VSOMEIP_WARNING << "debug diag job service available"; + } + // trigger offering of local services + call_debug_diag_job(true); + call_debug_diag_job(true); + VSOMEIP_WARNING << "Calling debug_diag_job_service (offer)"; + // wait until local services are available as well + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_local_services_available_) { + condition_.wait(its_lock); + } + wait_until_local_services_available_ = true; + VSOMEIP_WARNING << "local services available"; + } + + // check that from all services events were received + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_notifications_received_) { + condition_.wait(its_lock); + } + wait_until_notifications_received_ = true; + VSOMEIP_WARNING << "notifications received"; + } + + // tigger stop offering of local services + call_debug_diag_job(false); + VSOMEIP_WARNING << "Calling debug_diag_job_service (stop offer)"; + // wait until local services are unavailable + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_local_services_unavailable_) { + condition_.wait(its_lock); + } + wait_until_local_services_unavailable_ = true; + VSOMEIP_WARNING << "local services unavailable"; + } + + // trigger offering of local services + VSOMEIP_WARNING << "Calling debug_diag_job_service (offer)"; + call_debug_diag_job_wrong_message(true); + // wait until local services are available as well again + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_local_services_available_) { + condition_.wait(its_lock); + } + wait_until_local_services_available_ = true; + VSOMEIP_WARNING << "local services available"; + } + VSOMEIP_WARNING << "Calling shutdown method"; + call_shutdown_method(); + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_responses_received_) { + if (std::cv_status::timeout == + condition_.wait_for(its_lock, std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive shutdown responses within time"; + wait_until_responses_received_ = false; + } else { + VSOMEIP_WARNING << "received shutdown method responses"; + } + } + wait_until_responses_received_ = true; + } + { + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + stop_condition_.notify_one(); + } + } + +private: + std::array<debug_diag_job_plugin_test::service_info, 3> service_infos_local_; + std::array<debug_diag_job_plugin_test::service_info, 3> service_infos_remote_; + std::vector<debug_diag_job_plugin_test::service_info> service_infos_; + std::shared_ptr<vsomeip::application> app_; + std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, bool> other_services_available_; + std::map<std::pair<vsomeip::service_t, vsomeip::event_t>, std::uint32_t> other_services_received_notification_; + + std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, bool> other_remote_services_available_; + std::map<std::pair<vsomeip::service_t, vsomeip::event_t>, std::uint32_t> other_remote_services_received_notification_; + + std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, bool> other_local_services_available_; + std::map<std::pair<vsomeip::service_t, vsomeip::event_t>, std::uint32_t> other_local_services_received_notification_; + + std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, std::uint32_t> other_services_received_response_; + + bool wait_until_registered_; + bool wait_until_remote_services_available_; + bool wait_until_debug_diag_job_service_available_; + bool wait_until_local_services_available_; + bool wait_until_local_services_unavailable_; + bool wait_until_notifications_received_; + bool wait_until_responses_received_; + bool wait_until_debug_diag_job_response_received_; + std::mutex mutex_; + std::condition_variable condition_; + + bool wait_for_stop_; + + debug_diag_job_app_error_type_e last_response_; + std::mutex stop_mutex_; + std::condition_variable stop_condition_; + std::thread stop_thread_; + + std::thread run_thread_; +}; + +extern "C" void signal_handler(int signum) { + the_client->handle_signal(signum); +} + +TEST(someip_debug_diag_job_plugin_test, remotely_enable_offering_of_local_services) +{ + debug_diag_job_plugin_test_client its_sample( + debug_diag_job_plugin_test::service_infos_local, + debug_diag_job_plugin_test::service_infos_remote); +} + +#ifndef _WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_globals.hpp b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_globals.hpp new file mode 100644 index 0000000..eff3aa0 --- /dev/null +++ b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_globals.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DEBUG_DIAG_JOB_PLUGIN_TEST_GLOBALS_HPP_ +#define DEBUG_DIAG_JOB_PLUGIN_TEST_GLOBALS_HPP_ + +namespace debug_diag_job_plugin_test { + +struct service_info { + vsomeip::service_t service_id; + vsomeip::instance_t instance_id; + vsomeip::method_t method_id; + vsomeip::event_t event_id; + vsomeip::eventgroup_t eventgroup_id; +}; + +static constexpr std::array<service_info, 3> service_infos_remote = {{ + // placeholder to be consistent w/ client ids, service ids, app names + { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }, + { 0x1010, 0x1, 0x1010, 0x8111, 0x1 }, + { 0x2020, 0x1, 0x2020, 0x8222, 0x2 } +}}; + +static constexpr std::array<service_info, 3> service_infos_local = {{ + // placeholder to be consistent w/ client ids, service ids, app names + { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }, + { 0x3030, 0x1, 0x3030, 0x8333, 0x1 }, + { 0x4040, 0x1, 0x4040, 0x8444, 0x2 } +}}; + +static constexpr service_info debug_diag_job_serviceinfo = { 0xfea3, 0x80, 0x1, 0x0, 0x0 }; +static constexpr service_info debug_diag_job_serviceinfo_reset = { 0xfea3, 0x80, 0x2, 0x0, 0x0 }; + +static constexpr int notifications_to_send = 1; +} + +#endif /* DEBUG_DIAG_JOB_PLUGIN_TEST_GLOBALS_HPP_ */ diff --git a/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_master_starter.sh b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_master_starter.sh new file mode 100755 index 0000000..82963fb --- /dev/null +++ b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_master_starter.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +export VSOMEIP_CONFIGURATION=debug_diag_job_plugin_test_master.json +# start daemon +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(readlink -f ../plugins/mgu) +../daemon/./vsomeipd & +PID_VSOMEIPD=$! +# Start the services +# Array for service pids +SERVICE_PIDS=() +./debug_diag_job_plugin_test_service 1 & +SERVICE_PIDS+=($!) +./debug_diag_job_plugin_test_service 2 & +SERVICE_PIDS+=($!) + +print_starter_message () { + +if [ ! -z "$USE_LXC_TEST" ]; then + echo "starting initial event test on slave LXC with params $PASSED_SUBSCRIPTION_TYPE $CLIENT_JSON_FILE $REMAINING_OPTIONS" + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./debug_diag_job_plugin_test_slave_starter.sh\"" & +elif [ ! -z "$USE_DOCKER" ]; then + docker run --name ietms --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && ./debug_diag_job_plugin_test_slave_starter.sh" & +else +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** debug_diag_job_plugin_test_slave_starter.sh +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** debug_diag_job_plugin_test_master.json and +** debug_diag_job_plugin_test_slave.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message +fi +} +sleep 1 +print_starter_message + +FAIL=0 +# Wait until all clients are finished +for job in ${SERVICE_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +kill $PID_VSOMEIPD + +echo "" + +if [ ! -z "$USE_DOCKER" ]; then + docker stop ietms + docker rm ietms +fi + +# Check if both exited successfully +exit $FAIL diff --git a/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_service.cpp b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_service.cpp new file mode 100644 index 0000000..ed4bb7a --- /dev/null +++ b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_service.cpp @@ -0,0 +1,184 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "../debug_diag_job_plugin_tests/debug_diag_job_plugin_test_globals.hpp" + +static int service_number(0); + +class debug_diag_job_plugin_test_service { +public: + debug_diag_job_plugin_test_service(struct debug_diag_job_plugin_test::service_info _service_info_local, + struct debug_diag_job_plugin_test::service_info _service_info_remote, + std::uint32_t _events_to_offer) : + service_info_local_(_service_info_local), + service_info_remote_(_service_info_remote), + app_(vsomeip::runtime::get()->create_application()), + wait_until_registered_(true), + events_to_offer_(_events_to_offer), + offer_thread_(std::bind(&debug_diag_job_plugin_test_service::run, this)) { + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return; + } + app_->register_state_handler( + std::bind(&debug_diag_job_plugin_test_service::on_state, this, + std::placeholders::_1)); + app_->register_message_handler(service_info_local_.service_id, + service_info_local_.instance_id, service_info_local_.method_id, + std::bind(&debug_diag_job_plugin_test_service::on_shutdown_method_called, + this, std::placeholders::_1)); + app_->register_message_handler(service_info_remote_.service_id, + service_info_remote_.instance_id, service_info_remote_.method_id, + std::bind(&debug_diag_job_plugin_test_service::on_shutdown_method_called, + this, std::placeholders::_1)); + + for (auto si : {service_info_local_, service_info_remote_}) { + // offer field + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(si.eventgroup_id); + for (std::uint16_t i = 0; i < events_to_offer_; i++) { + app_->offer_event(si.service_id, si.instance_id, + static_cast<vsomeip::event_t>(si.event_id + i), its_eventgroups, true); + } + + // set value to field + std::shared_ptr<vsomeip::payload> its_payload = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data[2] = {static_cast<vsomeip::byte_t>((si.service_id & 0xFF00) >> 8), + static_cast<vsomeip::byte_t>((si.service_id & 0xFF))}; + its_payload->set_data(its_data, 2); + for (std::uint16_t i = 0; i < events_to_offer_; i++) { + app_->notify(si.service_id, si.instance_id, + static_cast<vsomeip::event_t>(si.event_id + i), its_payload); + } + } + + if (service_number == 1) { + // the debug mode service only needs to be offered one time + // debug mode service and event + + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(1); + app_->offer_event(0xfc10, 0x80, 0x8001, its_eventgroups, true); + std::shared_ptr<vsomeip::payload> its_payload = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t data[1] = {0x1}; + its_payload->set_data(data, 1); + app_->notify(0xfc10, 0x80, 0x8001, its_payload); + } + app_->start(); + } + + ~debug_diag_job_plugin_test_service() { + offer_thread_.join(); + } + + void offer() { + if (service_number == 1) { + // the debug mode service only needs to be offered one time + app_->offer_service(0xfc10, 0x80, 1, vsomeip::ANY_MINOR); + } + for (auto si : {service_info_local_, service_info_remote_}) { + app_->offer_service(si.service_id, si.instance_id); + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << si.service_id << "] Offering"; + } + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_shutdown_method_called(const std::shared_ptr<vsomeip::message> &_message) { + app_->send(vsomeip::runtime::get()->create_response(_message)); + static bool shutdown_called_local = false; + static bool shutdown_called_remote = false; + if (_message->get_method() == service_info_local_.method_id) { + shutdown_called_local = true; + } else if (_message->get_method() == service_info_remote_.method_id) { + shutdown_called_remote = true; + } + if (shutdown_called_local && shutdown_called_remote) { + VSOMEIP_WARNING << "Shutdown method called -> going down!"; + for (auto si : {service_info_local_, service_info_remote_}) { + app_->stop_offer_service(si.service_id, si.instance_id); + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') + << std::hex << si.service_id << "] Stop Offering"; + } + app_->clear_all_handler(); + app_->stop(); + } + } + + void run() { + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_local_.service_id << "] Running"; + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + offer(); + } + +private: + debug_diag_job_plugin_test::service_info service_info_local_; + debug_diag_job_plugin_test::service_info service_info_remote_; + std::shared_ptr<vsomeip::application> app_; + + bool wait_until_registered_; + std::uint32_t events_to_offer_; + std::mutex mutex_; + std::condition_variable condition_; + std::thread offer_thread_; +}; + +static std::uint32_t offer_multiple_events; + +TEST(someip_debug_diag_job_plugin_test, offer_one_remote_and_one_local_service) +{ + debug_diag_job_plugin_test_service its_sample( + debug_diag_job_plugin_test::service_infos_local[service_number], + debug_diag_job_plugin_test::service_infos_remote[service_number], + offer_multiple_events); +} + +#ifndef _WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cerr << "Please specify a service number and subscription type, like: " << argv[0] << " 2 SAME_SERVICE_ID" << std::endl; + std::cerr << "Valid service numbers are in the range of [1,2]" << std::endl; + return 1; + } + + service_number = std::stoi(std::string(argv[1]), nullptr); + + offer_multiple_events = 1; + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_slave_starter.sh b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_slave_starter.sh new file mode 100755 index 0000000..207ff64 --- /dev/null +++ b/test/debug_diag_job_plugin_tests/debug_diag_job_plugin_test_slave_starter.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +FAIL=0 + +export VSOMEIP_CONFIGURATION=debug_diag_job_plugin_test_slave.json + +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +# Start the services +export VSOMEIP_APPLICATION_NAME=debug_diag_job_plugin_test_client +./debug_diag_job_plugin_test_client & +PID_CLIENT=$! + +wait $PID_CLIENT || FAIL=$(($FAIL+1)) + +kill $PID_VSOMEIPD + +sleep 1 +echo "" + +# Check if both exited successfully +exit $FAIL diff --git a/test/e2e_tests/conf/e2e_test_client_external.json.in b/test/e2e_tests/conf/e2e_test_client_external.json.in new file mode 100644 index 0000000..d00c4bf --- /dev/null +++ b/test/e2e_tests/conf/e2e_test_client_external.json.in @@ -0,0 +1,83 @@ +{ + "unicast" : "@TEST_IP_MASTER@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "client-sample", + "id" : "0x1255" + } + ], + "services" : + [ + { + "service" : "0x1234", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + } + ], + "e2e" : + { + "e2e_enabled" : "true", + "protected" : + [ + { + "service_id" : "0x1234", + "event_id" : "0x8421", + "profile" : "CRC8", + "variant" : "checker", + "crc_offset" : "0", + "data_id_mode" : "3", + "data_length" : "56", + "data_id" : "0xA73" + }, + { + "service_id" : "0x1234", + "event_id" : "0x8001", + "profile" : "CRC8", + "variant" : "checker", + "crc_offset" : "0", + "data_id_mode" : "3", + "data_length" : "56", + "data_id" : "0xA73" + }, + { + "service_id" : "0x1234", + "event_id" : "0x6543", + "profile" : "CRC32", + "variant" : "checker", + "crc_offset" : "0" + }, + { + "service_id" : "0x1234", + "event_id" : "0x8002", + "profile" : "CRC32", + "variant" : "checker", + "crc_offset" : "0" + } + ] + }, + "routing" : "client-sample", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "2000", + "request_response_delay" : "1500" + } +} diff --git a/test/e2e_tests/conf/e2e_test_service_external.json.in b/test/e2e_tests/conf/e2e_test_service_external.json.in new file mode 100644 index 0000000..d2b8e04 --- /dev/null +++ b/test/e2e_tests/conf/e2e_test_service_external.json.in @@ -0,0 +1,66 @@ +{ + "unicast" : "@TEST_IP_SLAVE@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "service-sample", + "id" : "0x1277" + } + ], + "services" : + [ + { + "service" : "0x1234", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + } + ], + "e2e" : + { + "e2e_enabled" : "true", + "protected" : + [ + { + "service_id" : "0x1234", + "event_id" : "0x8001", + "profile" : "CRC8", + "variant" : "protector", + "crc_offset" : "0", + "data_id_mode" : "3", + "data_length" : "56", + "data_id" : "0xA73" + }, + { + "service_id" : "0x1234", + "event_id" : "0x8002", + "profile" : "CRC32", + "variant" : "protector", + "crc_offset" : "0" + } + ] + }, + "routing" : "service-sample", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "2000", + "request_response_delay" : "1500" + } +} diff --git a/test/e2e_tests/e2e_test_client.cpp b/test/e2e_tests/e2e_test_client.cpp new file mode 100644 index 0000000..ca136d9 --- /dev/null +++ b/test/e2e_tests/e2e_test_client.cpp @@ -0,0 +1,383 @@ +// Copyright (C) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "e2e_test_client.hpp" + +static bool is_remote_test = false; +static bool remote_client_allowed = true; +std::vector<std::vector<vsomeip::byte_t>> payloads_profile_01_; +std::vector<std::vector<vsomeip::byte_t>> event_payloads_profile_01_; + +std::vector<std::vector<vsomeip::byte_t>> payloads_custom_profile_; +std::vector<std::vector<vsomeip::byte_t>> event_payloads_custom_profile_; + +std::map<vsomeip::method_t, uint32_t> received_responses_counters_; + + +e2e_test_client::e2e_test_client(bool _test_external_communication, + bool _is_remote_client_allowed) + : app_(vsomeip::runtime::get()->create_application()), + is_available_(false), + sender_(std::bind(&e2e_test_client::run, this)), + received_responses_(0), + received_allowed_events_(0), + test_external_communication_(_test_external_communication), + is_remote_client_allowed_(_is_remote_client_allowed) { + +} + +bool e2e_test_client::init() { + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return false; + } + + app_->register_state_handler( + std::bind(&e2e_test_client::on_state, this, + std::placeholders::_1)); + + app_->register_message_handler(vsomeip::ANY_SERVICE, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, vsomeip::ANY_METHOD, + std::bind(&e2e_test_client::on_message, this, + std::placeholders::_1)); + + app_->register_availability_handler(vsomeip_test::TEST_SERVICE_SERVICE_ID, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, + std::bind(&e2e_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + return true; +} + +void e2e_test_client::start() { + VSOMEIP_INFO << "Starting..."; + app_->start(); +} + +void e2e_test_client::stop() { + VSOMEIP_INFO << "Stopping..."; + shutdown_service(); + app_->clear_all_handler(); + app_->stop(); +} + +void e2e_test_client::on_state(vsomeip::state_type_e _state) { + if(_state == vsomeip::state_type_e::ST_REGISTERED) { + app_->request_service(vsomeip_test::TEST_SERVICE_SERVICE_ID, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, false); + + // request events of eventgroup 0x01 which holds events 0x8001 (CRC8) + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(0x01); + + // request events of eventgroup 0x02 which holds events 0x8002 (CRC32) + std::set<vsomeip::eventgroup_t> its_eventgroups_2; + its_eventgroups_2.insert(0x02); + + app_->request_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), + its_eventgroups, true); + app_->request_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8002), + its_eventgroups_2, true); + app_->subscribe(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, 0x01); + app_->subscribe(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, 0x02); + } +} + +void e2e_test_client::on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + + VSOMEIP_INFO << std::hex << "Client 0x" << app_->get_client() + << " : Service [" << std::setw(4) << std::setfill('0') << std::hex + << _service << "." << _instance << "] is " + << (_is_available ? "available." : "NOT available."); + + // check that correct service / instance ID gets available + if (_is_available) { + EXPECT_EQ(vsomeip_test::TEST_SERVICE_SERVICE_ID, _service); + EXPECT_EQ(vsomeip_test::TEST_SERVICE_INSTANCE_ID, _instance); + } + + if(vsomeip_test::TEST_SERVICE_SERVICE_ID == _service + && vsomeip_test::TEST_SERVICE_INSTANCE_ID == _instance) { + std::unique_lock<std::mutex> its_lock(mutex_); + if(is_available_ && !_is_available) { + is_available_ = false; + } + else if(_is_available && !is_available_) { + is_available_ = true; + condition_.notify_one(); + } + } +} + +void e2e_test_client::on_message(const std::shared_ptr<vsomeip::message> &_response) { + VSOMEIP_INFO << "Received a response from Service [" + << std::setw(4) << std::setfill('0') << std::hex << _response->get_service() + << "." + << std::setw(4) << std::setfill('0') << std::hex << _response->get_instance() + << "] to Client/Session [" + << std::setw(4) << std::setfill('0') << std::hex << _response->get_client() + << "/" + << std::setw(4) << std::setfill('0') << std::hex << _response->get_session() + << "]"; + EXPECT_EQ(vsomeip_test::TEST_SERVICE_SERVICE_ID, _response->get_service()); + EXPECT_EQ(vsomeip_test::TEST_SERVICE_INSTANCE_ID, _response->get_instance()); + + // check fixed payload / CRC in response for service: 1234 method: 8421 + if (_response->get_message_type() == vsomeip::message_type_e::MT_RESPONSE + && vsomeip_test::TEST_SERVICE_METHOD_ID == _response->get_method()) { + // check for calculated CRC status OK for the predefined fixed payload sent by service + VSOMEIP_INFO << "Method ID 0x8421 -> IS_VALID_CRC 8 = " << std::hex << _response->is_valid_crc(); + EXPECT_EQ(true, _response->is_valid_crc()); + + // check if payload is as expected as well (including CRC / counter / data ID nibble) + std::shared_ptr<vsomeip::payload> pl = _response->get_payload(); + uint8_t* dataptr = pl->get_data(); //start after length field + for(uint32_t i = 0; i< pl->get_length(); i++) { + EXPECT_EQ(dataptr[i], payloads_profile_01_[received_responses_counters_[vsomeip_test::TEST_SERVICE_METHOD_ID] % vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND][i]); + } + received_responses_counters_[vsomeip_test::TEST_SERVICE_METHOD_ID]++; + } else if (_response->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION + && 0x8001 == _response->get_method()) { + // check CRC / payload calculated by sender for event 0x8001 against expected payload + // check for calculated CRC status OK for the calculated CRC / payload sent by service + VSOMEIP_INFO << "Event ID 0x8001 -> IS_VALID_CRC 8 = " << std::hex << _response->is_valid_crc(); + EXPECT_EQ(true, _response->is_valid_crc()); + + // check if payload is as expected as well (including CRC / counter / data ID nibble) + std::shared_ptr<vsomeip::payload> pl = _response->get_payload(); + uint8_t* dataptr = pl->get_data(); //start after length field + for(uint32_t i = 0; i< pl->get_length(); i++) { + EXPECT_EQ(dataptr[i], event_payloads_profile_01_[received_responses_counters_[0x8001] % vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND][i]); + } + received_responses_counters_[0x8001]++; + } else if (_response->get_message_type() == vsomeip::message_type_e::MT_RESPONSE + && 0x6543 == _response->get_method()) { + // check for calculated CRC status OK for the predefined fixed payload sent by service + VSOMEIP_INFO << "Method ID 0x6543 -> IS_VALID_CRC 32 = " << std::hex << _response->is_valid_crc(); + EXPECT_EQ(true, _response->is_valid_crc()); + + // check if payload is as expected as well (including CRC / counter / data ID nibble) + std::shared_ptr<vsomeip::payload> pl = _response->get_payload(); + uint8_t* dataptr = pl->get_data(); //start after length field + for(uint32_t i = 0; i< pl->get_length(); i++) { + EXPECT_EQ(dataptr[i], payloads_custom_profile_[received_responses_counters_[0x6543] % vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND][i]); + } + received_responses_counters_[0x6543]++; + } else if (_response->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION + && 0x8002 == _response->get_method()) { + VSOMEIP_INFO << "Event ID 0x8002 -> IS_VALID_CRC 32 = " << std::hex << _response->is_valid_crc(); + EXPECT_EQ(true, _response->is_valid_crc()); + + // check if payload is as expected as well (including CRC) + std::shared_ptr<vsomeip::payload> pl = _response->get_payload(); + uint8_t* dataptr = pl->get_data(); //start after length field + for(uint32_t i = 0; i< pl->get_length(); i++) { + EXPECT_EQ(dataptr[i], event_payloads_custom_profile_[received_responses_counters_[0x8002] % vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND][i]); + } + received_responses_counters_[0x8002]++; + } + + received_responses_++; + if (received_responses_ == vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND * 4) { + VSOMEIP_WARNING << std::hex << app_->get_client() + << ": Received all messages ~> going down!"; + } +} + +void e2e_test_client::run() { + for (uint32_t i = 0; i < vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND; ++i) { + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (!is_available_) + { + condition_.wait(its_lock); + } + } + auto request = vsomeip::runtime::get()->create_request(false); + request->set_service(vsomeip_test::TEST_SERVICE_SERVICE_ID); + request->set_instance(vsomeip_test::TEST_SERVICE_INSTANCE_ID); + + // send a request which is not e2e protected and expect an + // protected answer holding a fixed payload (profile 01 CRC8) + // this call triggers also an event 0x8001 which holds a calculated payload + request->set_method(vsomeip_test::TEST_SERVICE_METHOD_ID); + app_->send(request, true); + + // send a request which is not e2e protected and expect an + // protected answer holding a fixed payload (custom profile CRC32) + // this call triggers also an event 0x8002 which holds a calculated payload + request->set_method(0x6543); + app_->send(request, true); + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + stop(); +} + +void e2e_test_client::join_sender_thread() +{ + if (sender_.joinable()) { + sender_.join(); + } +} + +void e2e_test_client::shutdown_service() { + auto request = vsomeip::runtime::get()->create_request(false); + request->set_service(vsomeip_test::TEST_SERVICE_SERVICE_ID); + request->set_instance(vsomeip_test::TEST_SERVICE_INSTANCE_ID); + request->set_method(vsomeip_test::TEST_SERVICE_METHOD_ID_SHUTDOWN); + app_->send(request,true); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + + // expect 10 x response messages for both method IDs and events for both Event IDs + EXPECT_EQ(received_responses_, vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND * 4); + //EXPECT_EQ(received_allowed_events_, vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND); +} + +TEST(someip_e2e_test, basic_subscribe_request_response) +{ + e2e_test_client test_client(is_remote_test, remote_client_allowed); + if (test_client.init()) { + test_client.start(); + test_client.join_sender_thread(); + } +} + +int main(int argc, char** argv) { + + /* + e2e profile01 CRC8 protected fixed sample payloads sent by service + which must be received in client using the following config on client side: + "service_id" : "0x1234", + "event_id" : "0x8421", + "profile" : "CRC8", + "variant" : "checker", + "crc_offset" : "0", + "data_id_mode" : "3", + "data_length" : "56", + "data_id" : "0xA73" + */ + payloads_profile_01_.push_back({{0x82, 0xa4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); // initial event + payloads_profile_01_.push_back({{0x39, 0xa8, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x87, 0xa4, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x3c, 0xa8, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x55, 0xac, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x82, 0xa4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x39, 0xa8, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x87, 0xa4, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x3c, 0xa8, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x55, 0xac, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + + /* + e2e profile01 CRC8 protected payloads which shall be created by e2e module on + service side using the following config on client side: + "service_id" : "0x1234", + "event_id" : "0x8001", + "profile" : "CRC8", + "variant" : "checker", + "crc_offset" : "0", + "data_id_mode" : "3", + "data_length" : "56", + "data_id" : "0xA73" + */ + event_payloads_profile_01_.push_back({{0xa4, 0xa1, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff}}); // initial event + event_payloads_profile_01_.push_back({{0x05, 0xa2, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0x92, 0xa3, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0x5a, 0xa4, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0xc8, 0xa5, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0x69, 0xa6, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0xfe, 0xa7, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0xe4, 0xa8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0x7c, 0xa9, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff}}); + event_payloads_profile_01_.push_back({{0xdd, 0xaa, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff}}); + + /* + e2e custom profile CRR32 protected fixed sample payloads sent by service + which must be received in client using the following config on client side: + "service_id" : "0x1234", + "event_id" : "0x6543", + "profile" : "CRC32", + "variant" : "checker", + "crc_offset" : "0" + */ + payloads_custom_profile_.push_back({{0xa4, 0xb2, 0x75, 0x1f, 0xff, 0x00, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa5, 0x70, 0x1f, 0x28, 0xff, 0x01, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa7, 0x36, 0xa1, 0x71, 0xff, 0x02, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa6, 0xf4, 0xcb, 0x46, 0xff, 0x03, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa3, 0xbb, 0xdd, 0xc3, 0xff, 0x04, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa2, 0x79, 0xb7, 0xf4, 0xff, 0x05, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa0, 0x3f, 0x09, 0xad, 0xff, 0x06, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa1, 0xfd, 0x63, 0x9a, 0xff, 0x07, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xaa, 0xa1, 0x24, 0xa7, 0xff, 0x08, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xab, 0x63, 0x4e, 0x90, 0xff, 0x09, 0xff, 0x32}}); + + /* + e2e custom profile CRC32 protected payloads which shall be created by e2e module on + service side using the following config on client side for checking: + "service_id" : "0x1234", + "event_id" : "0x8002", + "profile" : "CRC32", + "variant" : "checker", + "crc_offset" : "0" + */ + event_payloads_custom_profile_.push_back({{0x89, 0x0e, 0xbc, 0x80, 0xff, 0xff, 0x00, 0x32}}); + event_payloads_custom_profile_.push_back({{0x90, 0x15, 0x8d, 0xc1, 0xff, 0xff, 0x01, 0x32}}); + event_payloads_custom_profile_.push_back({{0xbb, 0x38, 0xde, 0x02, 0xff, 0xff, 0x02, 0x32}}); + event_payloads_custom_profile_.push_back({{0xa2, 0x23, 0xef, 0x43, 0xff, 0xff, 0x03, 0x32}}); + event_payloads_custom_profile_.push_back({{0xed, 0x62, 0x79, 0x84, 0xff, 0xff, 0x04, 0x32}}); + event_payloads_custom_profile_.push_back({{0xf4, 0x79, 0x48, 0xc5, 0xff, 0xff, 0x05, 0x32}}); + event_payloads_custom_profile_.push_back({{0xdf, 0x54, 0x1b, 0x06, 0xff, 0xff, 0x06, 0x32}}); + event_payloads_custom_profile_.push_back({{0xc6, 0x4f, 0x2a, 0x47, 0xff, 0xff, 0x07, 0x32}}); + event_payloads_custom_profile_.push_back({{0x41, 0xd7, 0x36, 0x88, 0xff, 0xff, 0x08, 0x32}}); + event_payloads_custom_profile_.push_back({{0x58, 0xcc, 0x07, 0xc9, 0xff, 0xff, 0x09, 0x32}}); + + received_responses_counters_[vsomeip_test::TEST_SERVICE_METHOD_ID] = 0; + received_responses_counters_[0x8001] = 0; + received_responses_counters_[0x6543] = 0; + received_responses_counters_[0x8002] = 0; + + std::string test_remote("--remote"); + std::string test_local("--local"); + std::string test_allow_remote_client("--allow"); + std::string test_deny_remote_client("--deny"); + std::string help("--help"); + + int i = 1; + while (i < argc) + { + if(test_remote == argv[i]) + { + is_remote_test = true; + } + else if(test_local == argv[i]) + { + is_remote_test = false; + } + else if(test_allow_remote_client == argv[i]) + { + remote_client_allowed = true; + } + else if(test_deny_remote_client == argv[i]) + { + remote_client_allowed = false; + } + else if(help == argv[i]) + { + VSOMEIP_INFO << "Parameters:\n" + << "--remote: Run test between two hosts\n" + << "--local: Run test locally\n" + << "--allow: test is started with a policy that allows remote messages sent by this test client to the service\n" + << "--deny: test is started with a policy that denies remote messages sent by this test client to the service\n" + << "--help: print this help"; + } + i++; + } + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/e2e_tests/e2e_test_client.hpp b/test/e2e_tests/e2e_test_client.hpp new file mode 100644 index 0000000..378263c --- /dev/null +++ b/test/e2e_tests/e2e_test_client.hpp @@ -0,0 +1,55 @@ + +// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef E2E_TEST_CLIENT_HPP +#define E2E_TEST_CLIENT_HPP + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> + +#include "../someip_test_globals.hpp" + +#include <thread> +#include <mutex> +#include <condition_variable> +#include <atomic> + +class e2e_test_client { +public: + e2e_test_client(bool _test_external_communication, + bool _is_remote_client_allowed); + bool init(); + void start(); + void stop(); + + void on_state(vsomeip::state_type_e _state); + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available); + void on_message(const std::shared_ptr<vsomeip::message> &_response); + + void run(); + void join_sender_thread(); + +private: + void shutdown_service(); + + std::shared_ptr<vsomeip::application> app_; + + std::mutex mutex_; + std::condition_variable condition_; + bool is_available_; + + std::thread sender_; + + std::atomic<std::uint32_t> received_responses_; + std::atomic<std::uint32_t> received_allowed_events_; + + bool test_external_communication_; + bool is_remote_client_allowed_; +}; + +#endif // E2E_TEST_CLIENT_HPP diff --git a/test/e2e_tests/e2e_test_external_master_start.sh b/test/e2e_tests/e2e_test_external_master_start.sh new file mode 100755 index 0000000..c18f787 --- /dev/null +++ b/test/e2e_tests/e2e_test_external_master_start.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 1 ] +then + echo "Please pass a json file to this script" + echo "For example: $0 e2e_test_client_external.json" + exit 1 +fi + +MASTER_JSON_FILE=$1 +SERVICE_JSON_FILE=${MASTER_JSON_FILE/client/service} +ALLOW_DENY=$2 + +FAIL=0 + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=client-sample +./e2e_test_client --remote & +PID_CLIENT=$! + + +if [ ! -z "$USE_LXC_TEST" ]; then + echo "starting external e2e test on slave LXC" + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./e2e_test_external_slave_start.sh $SERVICE_JSON_FILE\"" & +elif [ ! -z "$USE_DOCKER" ]; then + docker run --name citms --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && ./e2e_test_external_slave_start.sh $SERVICE_JSON_FILE" & +else +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** e2e_test_external_slave_start.sh $SERVICE_JSON_FILE +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** e2e_test_service_external.json and +** e2e_test_client_external.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message +fi + +# Wait until client and service are finished +for client_pid in "${PID_CLIENT}" +do + if [ -n "$client_pid" ]; then + # Fail gets incremented if either client or service exit + # with a non-zero exit code + wait "$client_pid" || ((FAIL+=1)) + fi +done + +if [ ! -z "$USE_DOCKER" ]; then + docker stop citms + docker rm citms +fi + +kill $PID_CLIENT + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/e2e_tests/e2e_test_external_slave_start.sh b/test/e2e_tests/e2e_test_external_slave_start.sh new file mode 100755 index 0000000..ccf6fd5 --- /dev/null +++ b/test/e2e_tests/e2e_test_external_slave_start.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 1 ] +then + echo "Please pass a json file to this script" + echo "For example: $0 e2e_test_service_external.json" + exit 1 +fi + +FAIL=0 + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=service-sample +./e2e_test_service --remote & +PID_SERVICE=$! + +# Wait until client and service are finished +for client_pid in "${PID_SERVICE}" +do + if [ -n "$client_pid" ]; then + # Fail gets incremented if either client or service exit + # with a non-zero exit code + wait "$client_pid" || ((FAIL+=1)) + fi +done + +if [ ! -z "$USE_DOCKER" ]; then + docker stop citms + docker rm citms +fi + +kill $PID_SERVICE + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/e2e_tests/e2e_test_service.cpp b/test/e2e_tests/e2e_test_service.cpp new file mode 100644 index 0000000..886596e --- /dev/null +++ b/test/e2e_tests/e2e_test_service.cpp @@ -0,0 +1,296 @@ +// Copyright (C) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "e2e_test_service.hpp" + +static bool is_remote_test = false; +static bool remote_client_allowed = true; + +std::vector<std::vector<vsomeip::byte_t>> payloads_profile_01_; +std::vector<std::vector<vsomeip::byte_t>> payloads_custom_profile_; +std::map<vsomeip::method_t, uint32_t> received_requests_counters_; + +e2e_test_service::e2e_test_service() : + app_(vsomeip::runtime::get()->create_application()), + is_registered_(false), + blocked_(false), + number_of_received_messages_(0), + offer_thread_(std::bind(&e2e_test_service::run, this)) { +} + +bool e2e_test_service::init() { + std::lock_guard<std::mutex> its_lock(mutex_); + + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return false; + } + // profile01 CRC8 Method ID: 0x8421 + app_->register_message_handler(vsomeip_test::TEST_SERVICE_SERVICE_ID, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, vsomeip_test::TEST_SERVICE_METHOD_ID, + std::bind(&e2e_test_service::on_message, this, + std::placeholders::_1)); + + // custom profile CRC32 Method ID: 0x6543 + app_->register_message_handler(vsomeip_test::TEST_SERVICE_SERVICE_ID, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, 0x6543, + std::bind(&e2e_test_service::on_message, this, + std::placeholders::_1)); + + app_->register_message_handler(vsomeip_test::TEST_SERVICE_SERVICE_ID, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, + vsomeip_test::TEST_SERVICE_METHOD_ID_SHUTDOWN, + std::bind(&e2e_test_service::on_message_shutdown, this, + std::placeholders::_1)); + + app_->register_state_handler( + std::bind(&e2e_test_service::on_state, this, + std::placeholders::_1)); + + // offer field 0x8001 eventgroup 0x01 + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(0x01); + + // offer field 0x8002 eventgroup 0x02 + std::set<vsomeip::eventgroup_t> its_eventgroups_2; + its_eventgroups_2.insert(0x02); + + // profile01 CRC8 Event ID: 0x8001 + app_->offer_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), its_eventgroups, true); + + // set value to field which gets filled by e2e protection with CRC on sending + // after e2e protection the payload for first event should look like: + // {{0xa4, 0xa1, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff} + std::shared_ptr<vsomeip::payload> its_payload = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data[8] = {0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff}; + its_payload->set_data(its_data, 8); + + app_->notify(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), its_payload); + + // custom profile CRC32 Event ID: 0x8002 + app_->offer_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8002), its_eventgroups_2, true); + + // set value to field which gets filled by e2e protection with CRC on sending + // after e2e protection the payload for first event should look like: + // {{0x89, 0x0e, 0xbc, 0x80, 0xff, 0xff, 0x00, 0x32} + std::shared_ptr<vsomeip::payload> its_payload_8002 = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data_8002[8] = {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x32}; + its_payload_8002->set_data(its_data_8002, 8); + + app_->notify(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8002), its_payload_8002); + + return true; +} + +void e2e_test_service::start() { + VSOMEIP_INFO << "Starting..."; + app_->start(); +} + +void e2e_test_service::stop() { + VSOMEIP_INFO << "Stopping..."; + app_->clear_all_handler(); + app_->stop(); +} + +void e2e_test_service::join_offer_thread() { + if (offer_thread_.joinable()) { + offer_thread_.join(); + } +} + +void e2e_test_service::offer() { + app_->offer_service(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID); +} + +void e2e_test_service::stop_offer() { + app_->stop_offer_service(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID); +} + +void e2e_test_service::on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? "registered." : + "deregistered."); + + if(_state == vsomeip::state_type_e::ST_REGISTERED) { + if(!is_registered_) { + is_registered_ = true; + std::lock_guard<std::mutex> its_lock(mutex_); + blocked_ = true; + // "start" the run method thread + condition_.notify_one(); + } + } + else { + is_registered_ = false; + } +} + +void e2e_test_service::on_message(const std::shared_ptr<vsomeip::message>& _request) { + ASSERT_EQ(vsomeip_test::TEST_SERVICE_SERVICE_ID, _request->get_service()); + ASSERT_EQ(vsomeip_test::TEST_SERVICE_INSTANCE_ID, _request->get_instance()); + + VSOMEIP_INFO << "Received a message with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _request->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _request->get_session() << "] method: " << _request->get_method() ; + + std::shared_ptr<vsomeip::message> its_response = + vsomeip::runtime::get()->create_response(_request); + std::shared_ptr< vsomeip::payload > its_vsomeip_payload = + vsomeip::runtime::get()->create_payload(); + std::shared_ptr<vsomeip::payload> its_event_payload = + vsomeip::runtime::get()->create_payload(); + + // send fixed payload for profile 01 CRC8 + if (_request->get_method() == vsomeip_test::TEST_SERVICE_METHOD_ID) { + its_vsomeip_payload->set_data(payloads_profile_01_[received_requests_counters_[vsomeip_test::TEST_SERVICE_METHOD_ID] % vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND]); + its_response->set_payload(its_vsomeip_payload); + app_->send(its_response, true); + + // set value to field which gets filled by e2e protection with CRC on sending + vsomeip::byte_t its_data[8] = {0x00, 0x00, (uint8_t)received_requests_counters_[vsomeip_test::TEST_SERVICE_METHOD_ID], 0xff, 0xff, 0xff, 0xff, 0xff}; + its_event_payload->set_data(its_data, 8); + app_->notify(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), its_event_payload); + received_requests_counters_[vsomeip_test::TEST_SERVICE_METHOD_ID]++; + } else if (_request->get_method() == 0x6543) { + //send fixed payload for custom profile CRC32 + its_vsomeip_payload->set_data(payloads_custom_profile_[received_requests_counters_[0x6543] % vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND]); + its_response->set_payload(its_vsomeip_payload); + app_->send(its_response, true); + + // set value to field which gets filled by e2e protection with 4 byte CRC 32 on sending + vsomeip::byte_t its_data[8] = {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, (uint8_t)received_requests_counters_[0x6543], 0x32}; + its_event_payload->set_data(its_data, 8); + app_->notify(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8002), its_event_payload); + received_requests_counters_[0x6543]++; + } + + number_of_received_messages_++; + if(number_of_received_messages_ == vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND * 2) { + VSOMEIP_INFO << "Received all messages!"; + } +} + +void e2e_test_service::on_message_shutdown( + const std::shared_ptr<vsomeip::message>& _request) { + (void)_request; + VSOMEIP_INFO << "Shutdown method was called, going down now."; + stop(); +} + +void e2e_test_service::run() { + std::unique_lock<std::mutex> its_lock(mutex_); + while (!blocked_) + condition_.wait(its_lock); + offer(); +} + +TEST(someip_e2e_test, basic_subscribe_request_response) { + e2e_test_service test_service; + if (test_service.init()) { + test_service.start(); + test_service.join_offer_thread(); + } +} + +#ifndef _WIN32 +int main(int argc, char** argv) { + + /* + e2e profile01 CRC8 protected sample payloads using the following config at receiver: + "service_id" : "0x1234", + "event_id" : "0x8421", + "profile" : "CRC8", + "variant" : "checker", + "crc_offset" : "0", + "data_id_mode" : "3", + "data_length" : "56", + "data_id" : "0xA73" + */ + payloads_profile_01_.push_back({{0x82, 0xa4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x39, 0xa8, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x87, 0xa4, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x3c, 0xa8, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x55, 0xac, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x82, 0xa4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x39, 0xa8, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x87, 0xa4, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x3c, 0xa8, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + payloads_profile_01_.push_back({{0x55, 0xac, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff}}); + + /* + e2e custom profile CRC32 protected sample payloads using the following config at receiver: + "service_id" : "0x1234", + "event_id" : "0x6543", + "profile" : "CRC32", + "variant" : "checker", + "crc_offset" : "0" + */ + payloads_custom_profile_.push_back({{0xa4, 0xb2, 0x75, 0x1f, 0xff, 0x00, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa5, 0x70, 0x1f, 0x28, 0xff, 0x01, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa7, 0x36, 0xa1, 0x71, 0xff, 0x02, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa6, 0xf4, 0xcb, 0x46, 0xff, 0x03, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa3, 0xbb, 0xdd, 0xc3, 0xff, 0x04, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa2, 0x79, 0xb7, 0xf4, 0xff, 0x05, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa0, 0x3f, 0x09, 0xad, 0xff, 0x06, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xa1, 0xfd, 0x63, 0x9a, 0xff, 0x07, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xaa, 0xa1, 0x24, 0xa7, 0xff, 0x08, 0xff, 0x32}}); + payloads_custom_profile_.push_back({{0xab, 0x63, 0x4e, 0x90, 0xff, 0x09, 0xff, 0x32}}); + + received_requests_counters_[vsomeip_test::TEST_SERVICE_METHOD_ID] = 0; + received_requests_counters_[0x7654] = 0; + received_requests_counters_[0x6543] = 0; + received_requests_counters_[0x5432] = 0; + + std::string test_remote("--remote"); + std::string test_local("--local"); + std::string test_allow_remote_client("--allow"); + std::string test_deny_remote_client("--deny"); + std::string help("--help"); + + int i = 1; + while (i < argc) + { + if(test_remote == argv[i]) + { + is_remote_test = true; + } + else if(test_local == argv[i]) + { + is_remote_test = false; + } + else if(test_allow_remote_client == argv[i]) + { + remote_client_allowed = true; + } + else if(test_deny_remote_client == argv[i]) + { + remote_client_allowed = false; + } + else if(help == argv[i]) + { + VSOMEIP_INFO << "Parameters:\n" + << "--remote: Run test between two hosts\n" + << "--local: Run test locally\n" + << "--allow: test is started with a policy that allows remote messages sent by this test client to the service\n" + << "--deny: test is started with a policy that denies remote messages sent by this test client to the service\n" + << "--help: print this help"; + } + i++; + } + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/e2e_tests/e2e_test_service.hpp b/test/e2e_tests/e2e_test_service.hpp new file mode 100644 index 0000000..f7f2b96 --- /dev/null +++ b/test/e2e_tests/e2e_test_service.hpp @@ -0,0 +1,44 @@ +// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef e2e_test_SERVICE_HPP +#define e2e_test_SERVICE_HPP + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> + +#include "../someip_test_globals.hpp" + +#include <thread> +#include <mutex> +#include <condition_variable> + +class e2e_test_service { +public: + e2e_test_service(); + bool init(); + void start(); + void stop(); + void offer(); + void stop_offer(); + void join_offer_thread(); + void on_state(vsomeip::state_type_e _state); + void on_message(const std::shared_ptr<vsomeip::message> &_request); + void on_message_shutdown(const std::shared_ptr<vsomeip::message> &_request); + void run(); + +private: + std::shared_ptr<vsomeip::application> app_; + bool is_registered_; + + std::mutex mutex_; + std::condition_variable condition_; + bool blocked_; + std::uint32_t number_of_received_messages_; + std::thread offer_thread_; +}; + +#endif // E2E_TEST_SERVICE_HPP diff --git a/test/event_tests/conf/event_test_master.json.in b/test/event_tests/conf/event_test_master.json.in new file mode 100644 index 0000000..5190c87 --- /dev/null +++ b/test/event_tests/conf/event_test_master.json.in @@ -0,0 +1,32 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications" : + [ + { + "name" : "event_test_client", + "id" : "0x3489", + "max_dispatch_time" : "1000" + } + ], + "routing":"vsomeipd", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.50.1", + "port":"30490", + "protocol":"udp", + "cyclic_offer_delay" : "1000", + "ttl" : "3" + } +}
\ No newline at end of file diff --git a/test/event_tests/conf/event_test_slave_tcp.json.in b/test/event_tests/conf/event_test_slave_tcp.json.in new file mode 100644 index 0000000..57eba12 --- /dev/null +++ b/test/event_tests/conf/event_test_slave_tcp.json.in @@ -0,0 +1,43 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications" : + [ + { + "name" : "event_test_service", + "id" : "0x1210", + "max_dispatch_time" : "1000" + } + ], + "services": + [ + { + "service":"0x3344", + "instance":"0x0001", + "reliable": { + "port":"40001", + "enable-magic-cookies":"false" + } + } + ], + "routing":"vsomeipd", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.50.1", + "port":"30490", + "protocol":"udp", + "cyclic_offer_delay" : "1000", + "ttl" : "3" + } +}
\ No newline at end of file diff --git a/test/event_tests/conf/event_test_slave_udp.json.in b/test/event_tests/conf/event_test_slave_udp.json.in new file mode 100644 index 0000000..8071e04 --- /dev/null +++ b/test/event_tests/conf/event_test_slave_udp.json.in @@ -0,0 +1,40 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications" : + [ + { + "name" : "event_test_service", + "id" : "0x1210", + "max_dispatch_time" : "1000" + } + ], + "services": + [ + { + "service":"0x3344", + "instance":"0x0001", + "unreliable":"30001" + } + ], + "routing":"vsomeipd", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.50.1", + "port":"30490", + "protocol":"udp", + "cyclic_offer_delay" : "1000", + "ttl" : "3" + } +}
\ No newline at end of file diff --git a/test/event_tests/event_test_client.cpp b/test/event_tests/event_test_client.cpp new file mode 100644 index 0000000..fd65719 --- /dev/null +++ b/test/event_tests/event_test_client.cpp @@ -0,0 +1,283 @@ +// Copyright (C) 2014-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> +#include <atomic> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" +#include "../../implementation/configuration/include/internal.hpp" + +#include "event_test_globals.hpp" + +class event_test_client { +public: + event_test_client(struct event_test::service_info _service_info, event_test::test_mode_e _mode, + bool _use_tcp) : + service_info_(_service_info), + test_mode_(_mode), + use_tcp_(_use_tcp), + app_(vsomeip::runtime::get()->create_application("event_test_client")), + service_available_(false), + wait_until_registered_(true), + wait_until_service_available_(true), + wait_until_subscription_accepted_(true), + wait_until_events_received_(true), + wait_until_shutdown_reply_received_(true), + number_events_to_send_(50), + number_events_received_(0), + send_thread_(std::bind(&event_test_client::send, this)) { + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return; + } + app_->register_state_handler( + std::bind(&event_test_client::on_state, this, + std::placeholders::_1)); + + app_->register_message_handler(vsomeip::ANY_SERVICE, + vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, + std::bind(&event_test_client::on_message, this, + std::placeholders::_1)); + + // register availability for all other services and request their event. + app_->register_availability_handler(service_info_.service_id, + service_info_.instance_id, + std::bind(&event_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service(service_info_.service_id, + service_info_.instance_id); + + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(service_info_.eventgroup_id); + app_->request_event(service_info_.service_id, + service_info_.instance_id, service_info_.event_id, + its_eventgroups, false); + app_->register_subscription_status_handler(service_info_.service_id, + service_info_.instance_id, service_info_.eventgroup_id, + service_info_.event_id, + std::bind(&event_test_client::on_subscription_status_changed, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4, + std::placeholders::_5)); + app_->subscribe(service_info_.service_id, service_info_.instance_id, + service_info_.eventgroup_id, vsomeip::DEFAULT_MAJOR, + vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE); + + app_->start(); + } + + ~event_test_client() { + send_thread_.join(); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + VSOMEIP_INFO << "Service [" << std::setw(4) + << std::setfill('0') << std::hex << _service << "." << _instance + << "] is " << (_is_available ? "available":"not available") << "."; + if (_is_available) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_service_available_ = false; + condition_.notify_one(); + } + } + + void on_message(const std::shared_ptr<vsomeip::message> &_message) { + if(_message->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) { + on_notification(_message); + } else if (_message->get_message_type() == vsomeip::message_type_e::MT_RESPONSE) { + on_response(_message); + } + } + + void on_notification(const std::shared_ptr<vsomeip::message> &_message) { + EXPECT_EQ(service_info_.service_id, _message->get_service()); + EXPECT_EQ(service_info_.instance_id, _message->get_instance()); + EXPECT_EQ(service_info_.event_id, _message->get_method()); + if (test_mode_ == event_test::test_mode_e::PAYLOAD_FIXED) { + EXPECT_EQ(event_test::payload_fixed_length, _message->get_payload()->get_length()); + } else if (test_mode_ == event_test::test_mode_e::PAYLOAD_DYNAMIC) { + static vsomeip::length_t length_last_received_msg(0); + EXPECT_GT(_message->get_payload()->get_length(), length_last_received_msg); + length_last_received_msg = _message->get_payload()->get_length(); + + } + if (++number_events_received_ == number_events_to_send_) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_events_received_ = false; + condition_.notify_one(); + } + + VSOMEIP_DEBUG + << "Received a notification with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method() <<"]"; + + } + + void on_response(const std::shared_ptr<vsomeip::message> &_message) { + EXPECT_EQ(service_info_.service_id, _message->get_service()); + EXPECT_EQ(service_info_.shutdown_method_id, _message->get_method()); + EXPECT_EQ(service_info_.instance_id, _message->get_instance()); + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_shutdown_reply_received_ = false; + condition_.notify_one(); + } + + void on_subscription_status_changed(const vsomeip::service_t _service, + const vsomeip::instance_t _instance, + const vsomeip::eventgroup_t _eventgroup, + const vsomeip::event_t _event, + const uint16_t error_code) { + EXPECT_EQ(service_info_.service_id, _service); + EXPECT_EQ(service_info_.instance_id, _instance); + EXPECT_EQ(service_info_.eventgroup_id, _eventgroup); + EXPECT_EQ(service_info_.event_id, _event); + EXPECT_TRUE((error_code == 0x0u || error_code == 0x7u)); + if (error_code == 0x0u) { // accepted + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_subscription_accepted_ = false; + condition_.notify_one(); + } + } + + void send() { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + + while (wait_until_service_available_) { + condition_.wait(its_lock); + } + + while (wait_until_subscription_accepted_) { + if (std::cv_status::timeout == condition_.wait_for(its_lock, std::chrono::seconds(30))) { + VSOMEIP_ERROR << "Subscription wasn't accepted in time!"; + break; + } + } + + // call notify method + auto its_message = vsomeip::runtime::get()->create_request(use_tcp_); + its_message->set_service(service_info_.service_id); + its_message->set_instance(service_info_.instance_id); + its_message->set_method(service_info_.notify_method_id); + its_message->set_message_type(vsomeip::message_type_e::MT_REQUEST_NO_RETURN); + auto its_payload = vsomeip::runtime::get()->create_payload(); + its_payload->set_data(std::vector<vsomeip::byte_t>({ + static_cast<vsomeip::byte_t>(test_mode_), + static_cast<vsomeip::byte_t>(number_events_to_send_)})); + its_message->set_payload(its_payload); + app_->send(its_message); + + while (wait_until_events_received_) { + if (std::cv_status::timeout == condition_.wait_for(its_lock, std::chrono::seconds(30))) { + VSOMEIP_ERROR << "Didn't receive events in time!"; + break; + } + } + + // shutdown service + its_message->set_method(service_info_.shutdown_method_id); + its_message->set_message_type(vsomeip::message_type_e::MT_REQUEST); + app_->send(its_message); + + while (wait_until_shutdown_reply_received_) { + if (std::cv_status::timeout == condition_.wait_for(its_lock, std::chrono::seconds(30))) { + VSOMEIP_ERROR << "Shutdown request wasn't answered in time!"; + break; + } + } + VSOMEIP_INFO << "going down"; + app_->clear_all_handler(); + app_->stop(); + } + + +private: + struct event_test::service_info service_info_; + event_test::test_mode_e test_mode_; + bool use_tcp_; + std::shared_ptr<vsomeip::application> app_; + bool service_available_; + + bool wait_until_registered_; + bool wait_until_service_available_; + bool wait_until_subscription_accepted_; + bool wait_until_events_received_; + bool wait_until_shutdown_reply_received_; + std::mutex mutex_; + std::condition_variable condition_; + + const std::uint8_t number_events_to_send_; + std::atomic<std::uint32_t> number_events_received_; + std::thread send_thread_; +}; + +static event_test::test_mode_e passed_mode = event_test::test_mode_e::PAYLOAD_FIXED; +static bool use_tcp = false; + +TEST(someip_event_test, subscribe_or_call_method_at_service) +{ + event_test_client its_sample(event_test::service, passed_mode, use_tcp); +} + +#ifndef _WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if (argc < 3) { + std::cerr << "Please specify a operation mode, like: " << argv[0] << "PAYLOAD_FIXED TCP" << std::endl; + std::cerr << "Valid operation modes are PAYLOAD_FIXED and PAYLOAD_DYNAMIC" << std::endl; + std::cerr << "Valid communication modes are UDP or TCP" << std::endl; + return 1; + } + + if (std::string("PAYLOAD_FIXED") == std::string(argv[1])) { + passed_mode = event_test::test_mode_e::PAYLOAD_FIXED; + } else if (std::string("PAYLOAD_DYNAMIC") == std::string(argv[1])) { + passed_mode = event_test::test_mode_e::PAYLOAD_DYNAMIC; + } else { + std::cerr << "Wrong operation mode passed, exiting" << std::endl; + std::cerr << "Please specify a operation mode, like: " << argv[0] << " PAYLOAD_FIXED" << std::endl; + std::cerr << "Valid operation modes are PAYLOAD_FIXED and PAYLOAD_DYNAMIC" << std::endl; + return 1; + } + if (std::string("TCP")== std::string(argv[2])) { + use_tcp = true; + } else if (std::string("UDP")== std::string(argv[2])) { + use_tcp = false; + } + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/event_tests/event_test_globals.hpp b/test/event_tests/event_test_globals.hpp new file mode 100644 index 0000000..733c3c6 --- /dev/null +++ b/test/event_tests/event_test_globals.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EVENT_TEST_GLOBALS_HPP_ +#define EVENT_TEST_GLOBALS_HPP_ + +namespace event_test { + +struct service_info { + vsomeip::service_t service_id; + vsomeip::instance_t instance_id; + vsomeip::method_t method_id; + vsomeip::event_t event_id; + vsomeip::eventgroup_t eventgroup_id; + vsomeip::method_t shutdown_method_id; + vsomeip::method_t notify_method_id; +}; + +struct service_info service = { 0x3344, 0x1, 0x1111, 0x8002, 0x1, 0x1404, 0x4242 }; + +enum test_mode_e : std::uint8_t { + UNKNOWN, + PAYLOAD_FIXED, + PAYLOAD_DYNAMIC +}; + +std::uint32_t payload_fixed_length = 20; + +} + +#endif /* EVENT_TEST_GLOBALS_HPP_ */ diff --git a/test/event_tests/event_test_master_starter.sh b/test/event_tests/event_test_master_starter.sh new file mode 100755 index 0000000..6c39053 --- /dev/null +++ b/test/event_tests/event_test_master_starter.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +FAIL=0 + +if [ $# -lt 2 ] +then + echo "Please pass a operation and communication mode to this script." + echo "For example: $0 PAYLOAD_FIXED UDP" + echo "Valid operation modes include [PAYLOAD_FIXED, PAYLOAD_DYNAMIC]" + echo "Valid communication modes include [UDP, TCP]" + exit 1 +fi +TESTMODE=$1 +COMMUNICATIONMODE=$2 + +export VSOMEIP_CONFIGURATION=event_test_master.json +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +./event_test_client $TESTMODE $COMMUNICATIONMODE & +PID_CLIENT=$! + +sleep 1 + +if [ ! -z "$USE_LXC_TEST" ]; then + echo "starting offer test on slave LXC offer_test_external_slave_starter.sh" + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./event_test_slave_starter.sh $COMMUNICATIONMODE\"" & +elif [ ! -z "$USE_DOCKER" ]; then + docker run --name otems --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && sleep 10; ./event_test_slave_starter.sh $COMMUNICATIONMODE" & +else +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** event_test_slave_starter.sh $COMMUNICATIONMODE +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** event_test_slave_{udp,tcp}.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message +fi + +# Wait until all clients and services are finished +for job in $PID_CLIENT +do + # Fail gets incremented if a client exits with a non-zero exit code + echo "waiting for $job" + wait $job || FAIL=$(($FAIL+1)) +done + +kill $PID_VSOMEIPD +sleep 1 + +if [ ! -z "$USE_DOCKER" ]; then + docker stop otems + docker rm otems +fi + +# Check if everything went well +exit $FAIL diff --git a/test/event_tests/event_test_service.cpp b/test/event_tests/event_test_service.cpp new file mode 100644 index 0000000..271aa49 --- /dev/null +++ b/test/event_tests/event_test_service.cpp @@ -0,0 +1,184 @@ +// Copyright (C) 2014-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <chrono> +#include <condition_variable> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <atomic> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "event_test_globals.hpp" + +class event_test_service { +public: + event_test_service(struct event_test::service_info _service_info) : + service_info_(_service_info), + test_mode_(event_test::test_mode_e::UNKNOWN), + app_(vsomeip::runtime::get()->create_application("event_test_service")), + wait_until_registered_(true), + wait_until_notify_method_called_(true), + wait_until_shutdown_method_called_(true), + client_subscribed_(false), + notifications_to_send_(0), + offer_thread_(std::bind(&event_test_service::run, this)) { + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return; + } + app_->register_state_handler( + std::bind(&event_test_service::on_state, this, + std::placeholders::_1)); + + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(_service_info.eventgroup_id); + app_->offer_event(service_info_.service_id, service_info_.instance_id, + service_info_.event_id, its_eventgroups, false); + app_->register_message_handler(service_info_.service_id, + service_info_.instance_id, service_info_.shutdown_method_id, + std::bind(&event_test_service::on_shutdown_method_called, this, + std::placeholders::_1)); + app_->register_message_handler(service_info_.service_id, + service_info_.instance_id, service_info_.notify_method_id, + std::bind(&event_test_service::on_message, this, + std::placeholders::_1)); + app_->register_subscription_handler(service_info_.service_id, + service_info_.instance_id, service_info_.eventgroup_id, + std::bind(&event_test_service::subscription_handler, + this, std::placeholders::_1, std::placeholders::_2)); + + app_->start(); + } + + ~event_test_service() { + offer_thread_.join(); + } + + void offer() { + app_->offer_service(service_info_.service_id, service_info_.instance_id); + } + + void stop() { + app_->stop_offer_service(service_info_.service_id, service_info_.instance_id); + app_->clear_all_handler(); + app_->stop(); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_shutdown_method_called(const std::shared_ptr<vsomeip::message> &_message) { + app_->send(vsomeip::runtime::get()->create_response(_message)); + VSOMEIP_WARNING << "************************************************************"; + VSOMEIP_WARNING << "Shutdown method called -> going down!"; + VSOMEIP_WARNING << "************************************************************"; + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_shutdown_method_called_ = false; + condition_.notify_one(); + } + + void on_message(const std::shared_ptr<vsomeip::message> &_message) { + EXPECT_EQ(service_info_.service_id, _message->get_service()); + EXPECT_EQ(service_info_.instance_id, _message->get_instance()); + EXPECT_EQ(service_info_.notify_method_id, _message->get_method()); + auto its_payload = _message->get_payload(); + ASSERT_EQ(2u, its_payload->get_length()); + test_mode_ = static_cast<event_test::test_mode_e>(its_payload->get_data()[0]); + notifications_to_send_ = static_cast<std::uint32_t>(its_payload->get_data()[1]); + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_notify_method_called_ = false; + condition_.notify_one(); + } + + void run() { + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Running"; + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Offering"; + offer(); + + while (wait_until_notify_method_called_) { + condition_.wait(its_lock); + } + VSOMEIP_INFO << "notify"; + notify(); + + + while (wait_until_shutdown_method_called_) { + condition_.wait(its_lock); + } + its_lock.unlock(); + stop(); + } + + void notify() { + EXPECT_TRUE(client_subscribed_); + auto its_payload = vsomeip::runtime::get()->create_payload(); + for (std::uint32_t i = 0; i < notifications_to_send_; i++) { + if (test_mode_ == event_test::test_mode_e::PAYLOAD_FIXED) { + its_payload->set_data(std::vector<vsomeip::byte_t>(event_test::payload_fixed_length, 0x44)); + } else if (test_mode_ == event_test::test_mode_e::PAYLOAD_DYNAMIC) { + its_payload->set_data(std::vector<vsomeip::byte_t>(i+1, 0x55)); + } + app_->notify(service_info_.service_id, service_info_.instance_id, + service_info_.event_id, its_payload, false); + } + } + + bool subscription_handler(vsomeip::client_t _client, bool _subscribed) { + VSOMEIP_INFO << __func__ << ": client: 0x" << std::hex << _client + << ((_subscribed) ? " subscribed" : "unsubscribed"); + client_subscribed_ = _subscribed; + return true; + } + +private: + struct event_test::service_info service_info_; + event_test::test_mode_e test_mode_; + std::shared_ptr<vsomeip::application> app_; + + bool wait_until_registered_; + bool wait_until_notify_method_called_; + bool wait_until_shutdown_method_called_; + std::atomic<bool> client_subscribed_; + std::uint32_t notifications_to_send_; + std::mutex mutex_; + std::condition_variable condition_; + std::thread offer_thread_; +}; + +TEST(someip_event_test, send_events) +{ + event_test_service its_sample(event_test::service); +} + + +#ifndef _WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/event_tests/event_test_slave_starter.sh b/test/event_tests/event_test_slave_starter.sh new file mode 100755 index 0000000..9cc8e72 --- /dev/null +++ b/test/event_tests/event_test_slave_starter.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +FAIL=0 + +if [ $# -lt 1 ] +then + echo "Please pass a operation and communication mode to this script." + echo "For example: $0 UDP" + echo "Valid communication modes include [UDP, TCP]" + exit 1 +fi +COMMUNICATIONMODE=$1 + +if [ "$COMMUNICATIONMODE" = "TCP" ]; then + export VSOMEIP_CONFIGURATION=event_test_slave_tcp.json +elif [ "$COMMUNICATIONMODE" = "UDP" ]; then + export VSOMEIP_CONFIGURATION=event_test_slave_udp.json +fi + + +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +./event_test_service & +PID_SERVICE=$! + +# Wait until all clients and services are finished +for job in $PID_SERVICE +do + # Fail gets incremented if a client exits with a non-zero exit code + echo "waiting for $job" + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill $PID_VSOMEIPD +sleep 1 + +# Check if everything went well +exit $FAIL diff --git a/test/initial_event_tests/initial_event_test_client.cpp b/test/initial_event_tests/initial_event_test_client.cpp index d667e91..20f1f94 100644 --- a/test/initial_event_tests/initial_event_test_client.cpp +++ b/test/initial_event_tests/initial_event_test_client.cpp @@ -11,6 +11,7 @@ #include <thread> #include <map> #include <algorithm> +#include <atomic> #include <gtest/gtest.h> @@ -22,11 +23,11 @@ #include "../../implementation/logging/include/logger.hpp" #include "initial_event_test_globals.hpp" + class initial_event_test_client; static initial_event_test_client* the_client; extern "C" void signal_handler(int _signum); - class initial_event_test_client { public: initial_event_test_client(int _client_number, @@ -47,23 +48,14 @@ public: initial_event_strict_checking_(_initial_event_strict_checking), dont_exit_(_dont_exit), subscribe_only_one_(_subscribe_only_one), - stop_thread_(std::bind(&initial_event_test_client::wait_for_stop, this)) { + stop_thread_(&initial_event_test_client::wait_for_stop, this), + wait_for_signal_handler_registration_(true) + { if (!app_->init()) { ADD_FAILURE() << "Couldn't initialize application"; return; } - // register signal handler - the_client = this; - struct sigaction sa_new, sa_old; - sa_new.sa_handler = signal_handler; - sa_new.sa_flags = 0; - sigemptyset(&sa_new.sa_mask); - ::sigaction(SIGUSR1, &sa_new, &sa_old); - ::sigaction(SIGINT, &sa_new, &sa_old); - ::sigaction(SIGTERM, &sa_new, &sa_old); - ::sigaction(SIGABRT, &sa_new, &sa_old); - app_->register_state_handler( std::bind(&initial_event_test_client::on_state, this, std::placeholders::_1)); @@ -121,11 +113,27 @@ public: } } + // Block all signals + sigset_t mask; + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); + // start thread which handles all of the signals + signal_thread_ = std::thread(&initial_event_test_client::wait_for_signal, this); + { + std::unique_lock<std::mutex> its_lock(signal_mutex_); + while(wait_for_signal_handler_registration_) { + EXPECT_EQ(std::cv_status::no_timeout, + signal_condition_.wait_for(its_lock, std::chrono::seconds(10))); + } + wait_for_signal_handler_registration_ = true; + } + app_->start(); } ~initial_event_test_client() { stop_thread_.join(); + signal_thread_.join(); } void on_state(vsomeip::state_type_e _state) { @@ -222,7 +230,7 @@ public: break; } - if(notify && !dont_exit_) { + if (notify && !dont_exit_) { std::lock_guard<std::mutex> its_lock(stop_mutex_); wait_for_stop_ = false; stop_condition_.notify_one(); @@ -324,6 +332,41 @@ public: return false; } + void wait_for_signal() { + // register signal handler + the_client = this; + + sigset_t handler_mask; + sigemptyset(&handler_mask); + sigaddset(&handler_mask, SIGUSR1); + sigaddset(&handler_mask, SIGTERM); + sigaddset(&handler_mask, SIGINT); + sigaddset(&handler_mask, SIGABRT); + pthread_sigmask(SIG_UNBLOCK, &handler_mask, NULL); + + + struct sigaction sa_new, sa_old; + sa_new.sa_handler = signal_handler; + sa_new.sa_flags = 0; + sigemptyset(&sa_new.sa_mask); + ::sigaction(SIGUSR1, &sa_new, &sa_old); + ::sigaction(SIGINT, &sa_new, &sa_old); + ::sigaction(SIGTERM, &sa_new, &sa_old); + ::sigaction(SIGABRT, &sa_new, &sa_old); + + + + + { + std::lock_guard<std::mutex> its_lock(signal_mutex_); + wait_for_signal_handler_registration_ = false; + signal_condition_.notify_one(); + } + while (wait_for_stop_) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + void handle_signal(int _signum) { VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex << client_number_ << "] Catched signal, going down (" @@ -337,7 +380,7 @@ public: { std::unique_lock<std::mutex> its_lock(stop_mutex_); while (wait_for_stop_) { - stop_condition_.wait(its_lock); + stop_condition_.wait_for(its_lock, std::chrono::milliseconds(100)); } VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex << client_number_ @@ -366,7 +409,7 @@ private: std::mutex mutex_; std::condition_variable condition_; - bool wait_for_stop_; + std::atomic<bool> wait_for_stop_; bool subscribe_on_available_; std::uint32_t events_to_subscribe_; @@ -377,6 +420,10 @@ private: std::mutex stop_mutex_; std::condition_variable stop_condition_; std::thread stop_thread_; + bool wait_for_signal_handler_registration_; + std::mutex signal_mutex_; + std::condition_variable signal_condition_; + std::thread signal_thread_; }; static int client_number; @@ -411,7 +458,12 @@ TEST(someip_initial_event_test, wait_for_initial_events_of_all_services) #ifndef _WIN32 int main(int argc, char** argv) { + // Block all signals + sigset_t mask; + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); ::testing::InitGoogleTest(&argc, argv); + if(argc < 3) { std::cerr << "Please specify a client number and subscription type, like: " << argv[0] << " 2 UDP SUBSCRIBE_BEFORE_START SAME_SERVICE_ID" << std::endl; std::cerr << "Valid client numbers are from 0 to 0xFFFF" << std::endl; diff --git a/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in b/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in index 199f318..5072632 100755 --- a/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in +++ b/test/malicious_data_tests/conf/malicious_data_test_master_starter.sh.in @@ -12,12 +12,22 @@ FAIL=0 +if [ $# -lt 1 ] +then + echo "Please pass a test mode to this script." + echo "For example: $0 MALICIOUS_EVENTS" + echo "Valid subscription types include:" + echo " [MALICIOUS_EVENTS, PROTOCOL_VERSION, MESSAGE_TYPE, RETURN_CODE, WRONG_HEADER_FIELDS_UDP]" + exit 1 +fi +TESTMODE=$1 + export VSOMEIP_CONFIGURATION=malicious_data_test_master.json # start daemon ../daemon/./vsomeipd & PID_VSOMEIPD=$! # Start the services -./malicious_data_test_service & +./malicious_data_test_service $TESTMODE & PID_SERIVCE=$! sleep 1 @@ -26,16 +36,16 @@ if [ ! -z "$USE_LXC_TEST" ]; then echo "Waiting for 5s" sleep 5 echo "starting offer test on slave LXC offer_test_external_slave_starter.sh" - ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@\"" & + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ $TESTMODE\"" & echo "remote ssh pid: $!" elif [ ! -z "$USE_DOCKER" ]; then - docker run --name otems --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && sleep 10; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@" & + docker run --name otems --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && sleep 10; ./malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ $TESTMODE" & else cat <<End-of-message ******************************************************************************* ******************************************************************************* ** Please now run: -** malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ +** malicious_data_test_msg_sender @TEST_IP_MASTER@ @TEST_IP_SLAVE@ $TESTMODE ** from an external host to successfully complete this test. ** ** You probably will need to adapt the 'unicast' settings in diff --git a/test/malicious_data_tests/malicious_data_test_globals.hpp b/test/malicious_data_tests/malicious_data_test_globals.hpp index f6336a4..de14d5e 100644 --- a/test/malicious_data_tests/malicious_data_test_globals.hpp +++ b/test/malicious_data_tests/malicious_data_test_globals.hpp @@ -21,10 +21,11 @@ struct service_info { struct service_info service = { 0x3344, 0x1, 0x1111, 0x8002, 0x1, 0x1404, 0x4242 }; enum test_mode_e { - SUBSCRIBE, - SUBSCRIBE_UNSUBSCRIBE, - UNSUBSCRIBE, - SUBSCRIBE_UNSUBSCRIBE_NACK + MALICIOUS_EVENTS, + PROTOCOL_VERSION, + MESSAGE_TYPE, + RETURN_CODE, + WRONG_HEADER_FIELDS_UDP }; } diff --git a/test/malicious_data_tests/malicious_data_test_msg_sender.cpp b/test/malicious_data_tests/malicious_data_test_msg_sender.cpp index 27a62c2..84d09bf 100644 --- a/test/malicious_data_tests/malicious_data_test_msg_sender.cpp +++ b/test/malicious_data_tests/malicious_data_test_msg_sender.cpp @@ -23,7 +23,10 @@ #include "../../implementation/service_discovery/include/constants.hpp" #include "../../implementation/service_discovery/include/enumeration_types.hpp" #include "../../implementation/service_discovery/include/eventgroupentry_impl.hpp" +#include "../../implementation/service_discovery/include/serviceentry_impl.hpp" #include "../../implementation/message/include/message_impl.hpp" +#include "../../implementation/service_discovery/include/option_impl.hpp" +#include "../../implementation/service_discovery/include/ipv4_option_impl.hpp" #include "malicious_data_test_globals.hpp" static char* remote_address; @@ -258,31 +261,1330 @@ TEST_F(malicious_data, send_malicious_events) 30001); udp_socket.send_to(boost::asio::buffer(shutdown_call), target_service); } catch (const std::exception& _e) { - std::cout << "catched exception: " << _e.what(); - ASSERT_FALSE(true); + ADD_FAILURE() << "catched exception: " << _e.what(); } }); send_thread.join(); receive_thread.join(); - udp_socket.shutdown(boost::asio::socket_base::shutdown_both); - tcp_socket.shutdown(boost::asio::socket_base::shutdown_both); - udp_socket.close(); - tcp_socket.close(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); } +/* + * @test Send a message with an invalid protocol version to the test master + * two times as client and two time as service. Ensure that message with the + * WRONG_PROTOCOL_VERSION is sent back in both cases and that the client + * reestablishes the TCP connection after the service sent an message with an + * invalid protocol version back + */ +TEST_F(malicious_data, send_wrong_protocol_version) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::tcp::socket tcp_socket(io_); + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + std::promise<void> client_connected; + boost::asio::ip::tcp::socket::endpoint_type local( + boost::asio::ip::address::from_string(std::string(local_address)), + 40001); + boost::asio::ip::tcp::acceptor its_acceptor(io_); + boost::system::error_code ec; + its_acceptor.open(local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + its_acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + its_acceptor.bind(local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + its_acceptor.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); + its_acceptor.async_accept(tcp_socket, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_connected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk: " << _error.message(); + } + }); + + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x06, 0x9c, 0x41, + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client established TCP connection + if (std::future_status::timeout == client_connected.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't connect within time"; + } + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + std::atomic<std::uint32_t> fin_as_service_received(0); + std::promise<void> client_reconnected1; + + std::thread tcp_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + + while (keep_receiving) { + boost::system::error_code error; + tcp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received++; + keep_receiving = false; + } else { + keep_receiving = false; + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << error.message(); + return; + } + } + }); + + boost::asio::ip::tcp::socket tcp_socket3(io_); + its_acceptor.async_accept(tcp_socket3, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket3.set_option(boost::asio::ip::tcp::no_delay(true)); + client_reconnected1.set_value(); + } else { + ADD_FAILURE() << "accept_cbk2: " << _error.message(); + } + }); + // send malicious data as server (too long length and wrong protocol version) + std::uint8_t its_malicious_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xBB, 0xBB, 0xCA, 0xFE, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket.send(boost::asio::buffer(its_malicious_data)); + + // wait until client reestablished TCP connection + if (std::future_status::timeout == client_reconnected1.get_future().wait_for(std::chrono::seconds(10))) { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + ADD_FAILURE() << "Client didn't reconnect within time 1"; + } + + tcp_receive_thread.join(); + + EXPECT_EQ(1u, fin_as_service_received); + + std::thread tcp_receive_thread2([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + while (keep_receiving) { + boost::system::error_code error; + tcp_socket3.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received++; + // client must sent back error response before closing the connection + EXPECT_EQ(2u, fin_as_service_received); + if (fin_as_service_received == 2) { + keep_receiving = false; + } + } else { + keep_receiving = false; + return; + } + } + }); + + // send malicious data as server (wrong protocol version) + std::uint8_t its_malicious_data_correct_length[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xBB, 0xBB, 0xCA, 0xFE, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket3.send(boost::asio::buffer(its_malicious_data_correct_length)); + + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + + + tcp_receive_thread2.join(); + EXPECT_EQ(2u, fin_as_service_received); + + // establish second tcp connection as client and send malicious data as well + std::atomic<std::uint32_t> error_response_as_client_received(0); + std::atomic<std::uint32_t> fin_as_client_received(0); + + boost::asio::ip::tcp::socket tcp_socket2(io_); + boost::asio::ip::tcp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 40001); + tcp_socket2.open(remote.protocol()); + tcp_socket2.connect(remote); + + + std::thread tcp_service_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = tcp_socket2.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + std::shared_ptr<vsomeip::message> its_message(its_deserializer.deserialize_message()); + EXPECT_EQ(0x3345, its_message->get_service()); + EXPECT_EQ(0x1, its_message->get_method()); + EXPECT_EQ(0xCCCC, its_message->get_client()); + EXPECT_EQ(0xDDDD, its_message->get_session()); + EXPECT_EQ(vsomeip::return_code_e::E_WRONG_PROTOCOL_VERSION, its_message->get_return_code()); + EXPECT_EQ(vsomeip::message_type_e::MT_ERROR, its_message->get_message_type()); + error_response_as_client_received++; + // service must sent back error response before closing the connection + EXPECT_EQ(error_response_as_client_received - 1u, fin_as_client_received); + } else { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_client_received++; + // service must sent back error response before closing the connection + EXPECT_EQ(error_response_as_client_received, fin_as_client_received); + if (fin_as_client_received == 1) { + keep_receiving = false; + } + } + } + }); + + // send malicious data as client (too long length and wrong protocol version) + std::uint8_t its_malicious_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket2.send(boost::asio::buffer(its_malicious_client_data)); + tcp_service_receive_thread.join(); + EXPECT_EQ(1u, error_response_as_client_received); + EXPECT_EQ(1u, fin_as_client_received); + + tcp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket2.close(ec); + + // establish a tcp connection as client again and send wrong protocol + // version in message with a correct length field + boost::asio::ip::tcp::socket tcp_socket5(io_); + tcp_socket5.open(remote.protocol()); + tcp_socket5.connect(remote); + + std::thread tcp_service_receive_thread2([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = tcp_socket5.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + std::shared_ptr<vsomeip::message> its_message(its_deserializer.deserialize_message()); + EXPECT_EQ(0x3345, its_message->get_service()); + EXPECT_EQ(0x1, its_message->get_method()); + EXPECT_EQ(0xCCCC, its_message->get_client()); + EXPECT_EQ(0xDDDD, its_message->get_session()); + EXPECT_EQ(vsomeip::return_code_e::E_WRONG_PROTOCOL_VERSION, its_message->get_return_code()); + EXPECT_EQ(vsomeip::message_type_e::MT_ERROR, its_message->get_message_type()); + error_response_as_client_received++; + // service must sent back error response before closing the connection + EXPECT_EQ(error_response_as_client_received - 1u, fin_as_client_received); + if (error_response_as_client_received == 2) { + keep_receiving = false; + } + } + } + }); + + // send malicious data as client (wrong protocol version) + std::uint8_t its_malicious_client_data_correct_length[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + tcp_socket5.send(boost::asio::buffer(its_malicious_client_data_correct_length)); + tcp_service_receive_thread2.join(); + EXPECT_EQ(2u, error_response_as_client_received); + EXPECT_EQ(1u, fin_as_client_received); + + tcp_socket5.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket5.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); + +} + +/* + * @test Send a message with an invalid message type to the test master + * one time as client and one time as service. Ensure that the client + * reestablishes the TCP connection after the service sent an message with an + * invalid protocol version back + */ +TEST_F(malicious_data, send_wrong_message_type) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::tcp::socket tcp_socket(io_); + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + std::promise<void> client_connected; + boost::asio::ip::tcp::socket::endpoint_type local( + boost::asio::ip::address::from_string(std::string(local_address)), + 40001); + boost::asio::ip::tcp::acceptor its_acceptor(io_); + boost::system::error_code ec; + its_acceptor.open(local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + its_acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + its_acceptor.bind(local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + its_acceptor.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); + its_acceptor.async_accept(tcp_socket, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_connected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk: " << _error.message(); + } + }); + + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x06, 0x9c, 0x41, + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client established TCP connection + if (std::future_status::timeout == client_connected.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't connect within time"; + } + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + std::atomic<bool> fin_as_service_received(false); + std::promise<void> client_reconnected; + + std::thread tcp_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + + while (keep_receiving) { + boost::system::error_code error; + tcp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << error.message(); + } else if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received = true; + keep_receiving = false; + } else { + keep_receiving = false; + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << error.message(); + return; + } + } + }); + + boost::asio::ip::tcp::socket tcp_socket3(io_); + its_acceptor.async_accept(tcp_socket3, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_reconnected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk2: " << _error.message(); + } + }); + // send malicious data as server (too long length and wrong message type) + std::uint8_t its_malicious_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xBB, 0xBB, 0xCA, 0xFE, + 0x01, 0x00, 0xAA, 0x00 // message type set to 0xAA + }; + tcp_socket.send(boost::asio::buffer(its_malicious_data)); + + // wait until client reestablished TCP connection + if (std::future_status::timeout == client_reconnected.get_future().wait_for(std::chrono::seconds(10))) { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + ADD_FAILURE() << "Client didn't reconnect within time"; + } else { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + } + + tcp_receive_thread.join(); + + EXPECT_TRUE(fin_as_service_received); + + + // establish second tcp connection as client and send malicious data as well + std::atomic<bool> fin_as_client_received(false); + + boost::asio::ip::tcp::socket tcp_socket2(io_); + boost::asio::ip::tcp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 40001); + tcp_socket2.open(remote.protocol()); + tcp_socket2.connect(remote); + + + std::thread tcp_service_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::function<void()> receive; + std::vector<std::uint8_t> receive_buffer(4096); + + auto receive_cbk = [&](const boost::system::error_code& _error, + std::size_t bytes_transferred) { + (void)bytes_transferred; + if (!_error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << _error.message(); + } else { + EXPECT_EQ(boost::asio::error::eof, _error); + fin_as_client_received = true; + keep_receiving = false; + + } + }; + + receive = [&]() { + tcp_socket2.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()), + receive_cbk); + }; + + while (keep_receiving) { + receive(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + // send malicious data as client (too long length and wrong message type) + std::uint8_t its_malicious_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0xAA, 0x00 // protocol version set to 0xAA + }; + tcp_socket2.send(boost::asio::buffer(its_malicious_client_data)); + tcp_service_receive_thread.join(); + EXPECT_TRUE(fin_as_client_received); + + tcp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket2.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); + +} + +/* + * @test Send a message with an invalid return code to the test master + * one time as client and one time as service. Ensure that the client + * reestablishes the TCP connection after the service sent an message with an + * invalid protocol version back + */ +TEST_F(malicious_data, send_wrong_return_code) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::tcp::socket tcp_socket(io_); + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + std::promise<void> client_connected; + boost::asio::ip::tcp::socket::endpoint_type local( + boost::asio::ip::address::from_string(std::string(local_address)), + 40001); + boost::asio::ip::tcp::acceptor its_acceptor(io_); + boost::system::error_code ec; + its_acceptor.open(local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + its_acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + its_acceptor.bind(local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + its_acceptor.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); + its_acceptor.async_accept(tcp_socket, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_connected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk: " << _error.message(); + } + }); + + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x06, 0x9c, 0x41, + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client established TCP connection + if (std::future_status::timeout == client_connected.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't connect within time"; + } + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + std::atomic<bool> fin_as_service_received(false); + std::promise<void> client_reconnected; + + std::thread tcp_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + + while (keep_receiving) { + boost::system::error_code error; + tcp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (!error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << error.message(); + } else if (error == boost::asio::error::eof) { + EXPECT_EQ(boost::asio::error::eof, error); + fin_as_service_received = true; + keep_receiving = false; + } else { + keep_receiving = false; + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << error.message(); + return; + } + } + }); + + boost::asio::ip::tcp::socket tcp_socket3(io_); + its_acceptor.async_accept(tcp_socket3, [&](boost::system::error_code _error) { + if (!_error) { + // Nagle algorithm off + tcp_socket.set_option(boost::asio::ip::tcp::no_delay(true)); + client_reconnected.set_value(); + } else { + ADD_FAILURE() << "accept_cbk2: " << _error.message(); + } + }); + // send malicious data as server (too long length and wrong return code) + std::uint8_t its_malicious_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xBB, 0xBB, 0xCA, 0xFE, + 0x01, 0x00, 0x00, 0xAA // return code set to 0xAA + }; + tcp_socket.send(boost::asio::buffer(its_malicious_data)); + + // wait until client reestablished TCP connection + if (std::future_status::timeout == client_reconnected.get_future().wait_for(std::chrono::seconds(10))) { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + ADD_FAILURE() << "Client didn't reconnect within time"; + } else { + tcp_socket3.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket3.close(ec); + } + + tcp_receive_thread.join(); + + EXPECT_TRUE(fin_as_service_received); + + + // establish second tcp connection as client and send malicious data as well + std::atomic<bool> fin_as_client_received(false); + + boost::asio::ip::tcp::socket tcp_socket2(io_); + boost::asio::ip::tcp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 40001); + tcp_socket2.open(remote.protocol()); + tcp_socket2.connect(remote); + + + std::thread tcp_service_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::function<void()> receive; + std::vector<std::uint8_t> receive_buffer(4096); + + auto receive_cbk = [&](const boost::system::error_code& _error, + std::size_t bytes_transferred) { + (void)bytes_transferred; + if (!_error) { + ADD_FAILURE() << __func__ << ":" << __LINE__ + << " received a non-error:" << _error.message(); + } else { + EXPECT_EQ(boost::asio::error::eof, _error); + fin_as_client_received = true; + keep_receiving = false; + + } + }; + + receive = [&]() { + tcp_socket2.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()), + receive_cbk); + }; + + while (keep_receiving) { + receive(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + // send malicious data as client (too long length and wrong return code) + std::uint8_t its_malicious_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x07, 0x7f, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0x00, 0xAA // return version set to 0xAA + }; + tcp_socket2.send(boost::asio::buffer(its_malicious_client_data)); + tcp_service_receive_thread.join(); + EXPECT_TRUE(fin_as_client_received); + + tcp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket2.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + tcp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); + tcp_socket.close(ec); + +} + +/* + * @test Send a message with an invalid protocol version, invalid message type + * and invalid return code via UDP to the test master + * one time as client and one time as service. Ensure that message with the + * WRONG_PROTOCOL_VERSION is sent back in both cases + */ +TEST_F(malicious_data, wrong_header_fields_udp) +{ + std::promise<void> remote_client_subscribed; + std::promise<void> offer_received; + + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + boost::asio::ip::udp::socket udp_socket_service(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 40001)); + + boost::asio::ip::udp::endpoint udp_client_info; + boost::asio::ip::udp::endpoint udp_service_info; + + + std::thread sd_receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + std::atomic<bool> service_offered(false); + std::atomic<bool> client_subscribed(false); + + // join the sd multicast group 224.0.24.1 + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.24.1").to_v4())); + + while (keep_receiving) { + boost::system::error_code error; + std::size_t bytes_transferred = udp_socket.receive( + boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error); + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } else { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + for (auto e : sd_msg.get_entries()) { + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) { + EXPECT_TRUE(e->is_eventgroup_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type()); + EXPECT_EQ(1,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(1u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) { + std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e); + EXPECT_EQ(1u, its_casted_entry->get_eventgroup()); + } + std::shared_ptr<vsomeip::sd::option_impl> op = sd_msg.get_options().front(); + EXPECT_EQ(op->get_type(), vsomeip::sd::option_type_e::IP4_ENDPOINT); + EXPECT_EQ(op->get_length(), 9u); + if (op->get_type() == vsomeip::sd::option_type_e::IP4_ENDPOINT) { + std::shared_ptr<vsomeip::sd::ipv4_option_impl> ip_op + = std::static_pointer_cast<vsomeip::sd::ipv4_option_impl>(op); + EXPECT_EQ(vsomeip::sd::layer_four_protocol_e::UDP, ip_op->get_layer_four_protocol()); + udp_client_info = boost::asio::ip::udp::endpoint( + boost::asio::ip::address_v4(ip_op->get_address()), + ip_op->get_port()); + } + + remote_client_subscribed.set_value(); + client_subscribed = true; + } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(2,e->get_num_options(1)); + EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl()); + EXPECT_EQ(malicious_data_test::service.service_id + 1u, e->get_service()); + EXPECT_EQ(malicious_data_test::service.instance_id, e->get_instance()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) { + std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry = + std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e); + EXPECT_EQ(0u, its_casted_entry->get_minor_version()); + } + for (const auto op : sd_msg.get_options()) { + EXPECT_EQ(op->get_type(), vsomeip::sd::option_type_e::IP4_ENDPOINT); + EXPECT_EQ(op->get_length(), 9u); + if (op->get_type() == vsomeip::sd::option_type_e::IP4_ENDPOINT) { + std::shared_ptr<vsomeip::sd::ipv4_option_impl> ip_op + = std::static_pointer_cast<vsomeip::sd::ipv4_option_impl>(op); + if (ip_op->get_layer_four_protocol() == vsomeip::sd::layer_four_protocol_e::UDP) { + EXPECT_EQ(vsomeip::sd::layer_four_protocol_e::UDP, ip_op->get_layer_four_protocol()); + udp_service_info = boost::asio::ip::udp::endpoint( + boost::asio::ip::address_v4(ip_op->get_address()), + ip_op->get_port()); + } + } + } + offer_received.set_value(); + service_offered = true; + } + } + if (service_offered && client_subscribed) { + keep_receiving = false; + } + } else { + ADD_FAILURE() << " received non-sd message"; + } + } + } + }); + + std::thread send_thread([&]() { + try { + boost::system::error_code ec; + udp_socket_service.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "udp_socket_service set_option"); + + // offer the service + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x30, // length + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, // length entries array + 0x01, 0x00, 0x00, 0x20, + 0x33, 0x44, 0x00, 0x01, // service / instance + 0x00, 0xff, 0xff, 0xff, // major / ttl + 0x00, 0x00, 0x00, 0x00, // minor + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // slave address + 0x00, 0x11, 0x9c, 0x41, // offer via udp + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_offer_service_message[48], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + + // wait until client subscribed + if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Client didn't subscribe within time"; + } + + // wait until a offer was received + if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive offer within time"; + } + + // send malicious data as server (wrong protocol version) + std::uint8_t wrong_protocol_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xAA, 0xAA, 0xCA, 0xFE, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + udp_socket_service.send_to(boost::asio::buffer(wrong_protocol_data), udp_client_info); + + std::uint8_t wrong_message_type_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xBB, 0xBB, 0xCA, 0xFE, + 0x01, 0x00, 0xBB, 0x00 // message type set to 0xBB + }; + udp_socket_service.send_to(boost::asio::buffer(wrong_message_type_data), udp_client_info); + + std::uint8_t wrong_return_code_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xCA, 0xFE, + 0x01, 0x00, 0x00, 0xCC // return code set to 0xCC + }; + udp_socket_service.send_to(boost::asio::buffer(wrong_return_code_data), udp_client_info); + + std::uint8_t all_wrong_data[] = { + 0x33, 0x44, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xAA, 0xAA, 0xCA, 0xFE, + 0xAA, 0x00, 0xBB, 0xCC + }; + udp_socket_service.send_to(boost::asio::buffer(all_wrong_data), udp_client_info); + + udp_socket_service.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket_service.close(ec); + + // establish second UDP connection as client and send malicious data as well + std::atomic<bool> error_response_as_client_received(false); + + boost::asio::ip::udp::socket udp_socket_client(io_); + boost::asio::ip::udp::socket::endpoint_type remote( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + udp_socket_client.open(remote.protocol()); + udp_socket_client.connect(remote); + + + std::thread udp_client_receive_thread([&]() { + std::atomic<bool> keep_receiving(true); + std::function<void()> receive; + std::vector<std::uint8_t> receive_buffer(4096); + + auto receive_cbk = [&](const boost::system::error_code& _error, + std::size_t bytes_transferred) { + if (!_error) { + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + std::shared_ptr<vsomeip::message> its_message(its_deserializer.deserialize_message()); + EXPECT_EQ(0x3345, its_message->get_service()); + EXPECT_EQ(0x1, its_message->get_method()); + EXPECT_EQ(0xCCCC, its_message->get_client()); + EXPECT_EQ(0xDDDD, its_message->get_session()); + EXPECT_EQ(vsomeip::return_code_e::E_WRONG_PROTOCOL_VERSION, its_message->get_return_code()); + EXPECT_EQ(vsomeip::message_type_e::MT_ERROR, its_message->get_message_type()); + error_response_as_client_received = true; + keep_receiving = false; + } else { + ADD_FAILURE() << __func__ << ":" << __LINE__ << " error: " << _error.message(); + return; + } + }; + + receive = [&]() { + udp_socket_client.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()), + receive_cbk); + }; + + while (keep_receiving) { + receive(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + // send malicious data as client (too long length and wrong protocol version) + std::uint8_t wrong_protocol_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0x00, 0x00 // protocol version set to 0xAA + }; + udp_socket_client.send_to(boost::asio::buffer(wrong_protocol_client_data), udp_service_info); + + std::uint8_t wrong_message_type_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0xBB, 0x00 // message type set to 0xBB + }; + udp_socket_client.send_to(boost::asio::buffer(wrong_message_type_client_data), udp_service_info); + + std::uint8_t wrong_return_code_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0x01, 0x00, 0x00, 0xCC // return code set to 0xCC + }; + udp_socket_client.send_to(boost::asio::buffer(wrong_return_code_client_data), udp_service_info); + + std::uint8_t all_wrong_client_data[] = { + 0x33, 0x45, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x08, + 0xCC, 0xCC, 0xDD, 0xDD, + 0xAA, 0x00, 0xBB, 0xCC + }; + udp_socket_client.send_to(boost::asio::buffer(all_wrong_client_data), udp_service_info); + + + udp_client_receive_thread.join(); + EXPECT_TRUE(error_response_as_client_received); + + udp_socket_client.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket_client.close(ec); + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x33, 0x45, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4()); + udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service); + udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket2.close(ec); + } catch (const std::exception& _e) { + ADD_FAILURE() << "catched exception: " << _e.what(); + } + + }); + + send_thread.join(); + sd_receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); +} + #ifndef _WIN32 int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); if(argc < 3) { - std::cerr << "Please pass an target and local IP address to this binary like: " - << argv[0] << " 10.0.3.1 10.0.3.202" << std::endl; + std::cerr << "Please pass an target, local IP address and test mode to this binary like: " + << argv[0] << " 10.0.3.1 10.0.3.202 EVENTS" << std::endl; exit(1); } remote_address = argv[1]; local_address = argv[2]; + std::string its_testmode = argv[3]; + if (its_testmode == std::string("MALICIOUS_EVENTS")) { + ::testing::GTEST_FLAG(filter) = "*send_malicious_events"; + } else if (its_testmode == std::string("PROTOCOL_VERSION")) { + ::testing::GTEST_FLAG(filter) = "*send_wrong_protocol_version"; + } else if (its_testmode == std::string("MESSAGE_TYPE")) { + ::testing::GTEST_FLAG(filter) = "*send_wrong_message_type"; + } else if (its_testmode == std::string("RETURN_CODE")) { + ::testing::GTEST_FLAG(filter) = "*send_wrong_return_code"; + } else if (its_testmode == std::string("WRONG_HEADER_FIELDS_UDP")) { + ::testing::GTEST_FLAG(filter) = "*wrong_header_fields_udp"; + } return RUN_ALL_TESTS(); } #endif diff --git a/test/malicious_data_tests/malicious_data_test_service.cpp b/test/malicious_data_tests/malicious_data_test_service.cpp index 17a4bb8..cc4999e 100644 --- a/test/malicious_data_tests/malicious_data_test_service.cpp +++ b/test/malicious_data_tests/malicious_data_test_service.cpp @@ -61,15 +61,20 @@ public: // request service of client app_->request_service(service_info_.service_id, service_info_.instance_id); app_->subscribe(service_info_.service_id, service_info_.instance_id, - service_info_.eventgroup_id, 0, vsomeip::subscription_type_e::SU_RELIABLE, + service_info_.eventgroup_id, 0, + (testmode_ == malicious_data_test::test_mode_e::WRONG_HEADER_FIELDS_UDP) ? + vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE : + vsomeip::subscription_type_e::SU_RELIABLE, service_info_.event_id); app_->start(); } ~malicious_data_test_service() { - EXPECT_EQ(9u, received_events_); - EXPECT_EQ(9u, received_methodcalls_); + if (testmode_ == malicious_data_test::test_mode_e::MALICIOUS_EVENTS) { + EXPECT_EQ(9u, received_events_); + EXPECT_EQ(9u, received_methodcalls_); + } offer_thread_.join(); } @@ -153,7 +158,7 @@ private: std::thread offer_thread_; }; -malicious_data_test::test_mode_e its_testmode(malicious_data_test::test_mode_e::SUBSCRIBE); +malicious_data_test::test_mode_e its_testmode(malicious_data_test::test_mode_e::MALICIOUS_EVENTS); TEST(someip_malicious_data_test, block_subscription_handler) { @@ -165,6 +170,18 @@ TEST(someip_malicious_data_test, block_subscription_handler) int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + std::string its_passed_testmode = argv[1]; + if (its_passed_testmode == std::string("MALICIOUS_EVENTS")) { + its_testmode = malicious_data_test::test_mode_e::MALICIOUS_EVENTS; + } else if (its_passed_testmode == std::string("PROTOCOL_VERSION")) { + its_testmode = malicious_data_test::test_mode_e::PROTOCOL_VERSION; + } else if (its_passed_testmode == std::string("MESSAGE_TYPE")) { + its_testmode = malicious_data_test::test_mode_e::MESSAGE_TYPE; + } else if (its_passed_testmode == std::string("RETURN_CODE")) { + its_testmode = malicious_data_test::test_mode_e::RETURN_CODE; + } else if (its_passed_testmode == std::string("WRONG_HEADER_FIELDS_UDP")) { + its_testmode = malicious_data_test::test_mode_e::WRONG_HEADER_FIELDS_UDP; + } return RUN_ALL_TESTS(); } #endif diff --git a/test/pending_subscription_tests/conf/pending_subscription_test_master_starter.sh.in b/test/pending_subscription_tests/conf/pending_subscription_test_master_starter.sh.in index 2d9311c..c36f14d 100755 --- a/test/pending_subscription_tests/conf/pending_subscription_test_master_starter.sh.in +++ b/test/pending_subscription_tests/conf/pending_subscription_test_master_starter.sh.in @@ -17,7 +17,7 @@ then echo "Please pass a test mode to this script." echo "For example: $0 SUSCRIBE" echo "Valid subscription types include:" - echo " [SUBSCRIBE, SUBSCRIBE_UNSUBSCRIBE, UNSUBSCRIBE, SUBSCRIBE_UNSUBSCRIBE_NACK, SUBSCRIBE_UNSUBSCRIBE_SAME_PORT, SUBSCRIBE_RESUBSCRIBE_MIXED, SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE]" + echo " [SUBSCRIBE, SUBSCRIBE_UNSUBSCRIBE, UNSUBSCRIBE, SUBSCRIBE_UNSUBSCRIBE_NACK, SUBSCRIBE_UNSUBSCRIBE_SAME_PORT, SUBSCRIBE_RESUBSCRIBE_MIXED, SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE, REQUEST_TO_SD]" exit 1 fi TESTMODE=$1 diff --git a/test/pending_subscription_tests/pending_subscription_test_globals.hpp b/test/pending_subscription_tests/pending_subscription_test_globals.hpp index 7fa5480..59ad88e 100644 --- a/test/pending_subscription_tests/pending_subscription_test_globals.hpp +++ b/test/pending_subscription_tests/pending_subscription_test_globals.hpp @@ -27,7 +27,8 @@ enum test_mode_e { SUBSCRIBE_UNSUBSCRIBE_NACK, SUBSCRIBE_UNSUBSCRIBE_SAME_PORT, SUBSCRIBE_RESUBSCRIBE_MIXED, - SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE + SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE, + REQUEST_TO_SD }; } diff --git a/test/pending_subscription_tests/pending_subscription_test_sd_msg_sender.cpp b/test/pending_subscription_tests/pending_subscription_test_sd_msg_sender.cpp index d8108a5..9668c66 100644 --- a/test/pending_subscription_tests/pending_subscription_test_sd_msg_sender.cpp +++ b/test/pending_subscription_tests/pending_subscription_test_sd_msg_sender.cpp @@ -58,6 +58,7 @@ TEST_F(pending_subscription, send_multiple_subscriptions) boost::asio::ip::udp::socket udp_socket(io_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); std::thread receive_thread([&](){ std::atomic<bool> keep_receiving(true); @@ -227,6 +228,7 @@ TEST_F(pending_subscription, send_alternating_subscribe_unsubscribe) boost::asio::ip::udp::socket udp_socket(io_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); std::thread receive_thread([&](){ const std::uint32_t expected_acks(8); @@ -423,6 +425,7 @@ TEST_F(pending_subscription, send_multiple_unsubscriptions) boost::asio::ip::udp::socket udp_socket(io_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); std::thread receive_thread([&](){ const std::uint32_t expected_acks(2); @@ -619,6 +622,7 @@ TEST_F(pending_subscription, send_alternating_subscribe_nack_unsubscribe) boost::asio::ip::udp::socket udp_socket(io_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); std::thread receive_thread([&](){ const std::uint32_t expected_acks(8); @@ -826,9 +830,11 @@ TEST_F(pending_subscription, send_alternating_subscribe_unsubscribe_same_port) boost::asio::ip::udp::socket udp_socket(io_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); boost::asio::ip::tcp::socket tcp_socket(io_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 30490)); tcp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + tcp_socket.set_option(boost::asio::socket_base::linger(true, 0)); std::thread receive_thread([&](){ const std::uint32_t expected_acks(8); @@ -1047,6 +1053,7 @@ TEST_F(pending_subscription, subscribe_resubscribe_mixed) boost::asio::ip::udp::socket udp_socket(io_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); std::thread receive_thread([&](){ std::atomic<bool> keep_receiving(true); @@ -1247,9 +1254,11 @@ TEST_F(pending_subscription, send_subscribe_stop_subscribe_subscribe) boost::asio::ip::udp::socket udp_socket(io_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); boost::asio::ip::tcp::socket tcp_socket(io_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 30490)); tcp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + tcp_socket.set_option(boost::asio::socket_base::linger(true, 0)); std::thread receive_thread([&](){ const std::uint32_t expected_acks(1); @@ -1440,6 +1449,179 @@ TEST_F(pending_subscription, send_subscribe_stop_subscribe_subscribe) udp_socket.close(ec); } +/* + * @test Send a message with message type 0x0 (REQUEST) to the remote SD port + * and check if the remote SD continues to send offers + */ +TEST_F(pending_subscription, send_request_to_sd_port) +{ + std::promise<bool> all_offers_received; + + boost::asio::ip::udp::socket udp_socket(io_, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + udp_socket.set_option(boost::asio::ip::multicast::enable_loopback(false)); + udp_socket.set_option(boost::asio::ip::multicast::join_group( + boost::asio::ip::address::from_string("224.0.23.1").to_v4())); + udp_socket.set_option(boost::asio::socket_base::reuse_address(true)); + udp_socket.set_option(boost::asio::socket_base::linger(true, 0)); + + std::thread receive_thread([&](){ + std::atomic<bool> keep_receiving(true); + std::function<void()> receive; + std::vector<std::uint8_t> receive_buffer(4096); + std::vector<vsomeip::event_t> its_received_events; + + const std::function<void(const boost::system::error_code&, std::size_t)> receive_cbk = [&]( + const boost::system::error_code& error, std::size_t bytes_transferred) { + if (error) { + keep_receiving = false; + ADD_FAILURE() << __func__ << " error: " << error.message(); + return; + } + #if 0 + std::stringstream str; + for (size_t i = 0; i < bytes_transferred; i++) { + str << std::hex << std::setw(2) << std::setfill('0') << std::uint32_t(receive_buffer[i]) << " "; + } + std::cout << __func__ << " received: " << std::dec << bytes_transferred << " bytes: " << str.str() << std::endl; + #endif + + static int offers_received = 0; + static int responses_received = 0; + vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0); + vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN], + receive_buffer[VSOMEIP_SERVICE_POS_MAX]); + vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN], + receive_buffer[VSOMEIP_METHOD_POS_MAX]); + if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) { + vsomeip::sd::message_impl sd_msg; + EXPECT_TRUE(sd_msg.deserialize(&its_deserializer)); + EXPECT_EQ(1u, sd_msg.get_entries().size()); + EXPECT_EQ(2u, sd_msg.get_options().size()); + for (auto e : sd_msg.get_entries()) { + EXPECT_TRUE(e->is_service_entry()); + EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type()); + EXPECT_EQ(0xffffffu, e->get_ttl()); + EXPECT_EQ(pending_subscription_test::service.service_id, e->get_service()); + EXPECT_EQ(pending_subscription_test::service.instance_id, e->get_instance()); + offers_received++; + } + } else { // non-sd-message + vsomeip::message_impl msg; + EXPECT_TRUE(msg.deserialize(&its_deserializer)); + if (msg.get_message_type() == vsomeip::message_type_e::MT_RESPONSE) { + EXPECT_EQ(vsomeip::message_type_e::MT_RESPONSE, msg.get_message_type()); + EXPECT_EQ(pending_subscription_test::service.service_id, msg.get_service()); + EXPECT_EQ(pending_subscription_test::service.shutdown_method_id, msg.get_method()); + EXPECT_EQ(0x2222, msg.get_client()); + responses_received++; + } + } + + if (responses_received == 1) { // resonse to shutdown method was received as well + keep_receiving = false; + } else if (offers_received == 3 ) { // all multiple offers received + try { + all_offers_received.set_value(true); + } catch (const std::future_error& e) { + + } + } + if (!error && keep_receiving) { + receive(); + } + }; + + receive = [&]() { + udp_socket.async_receive(boost::asio::buffer(receive_buffer, receive_buffer.capacity()), + receive_cbk); + }; + + receive(); + while(keep_receiving) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + std::thread send_thread([&]() { + try { + std::uint8_t its_subscribe_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x40, // length + 0x00, 0x00, 0x10, 0x01, + 0x01, 0x01, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, // message type is set to 0x0 (REQUEST) + 0x00, 0x00, 0x00, 0x20, // length entries array + 0x06, 0x00, 0x00, 0x10, + 0x11, 0x22, 0x00, 0x01, // service / instance + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x10, 0x00, // eventgroup + 0x06, 0x00, 0x00, 0x10, + 0x11, 0x22, 0x00, 0x01, // service / instance + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x10, 0x01, // eventgroup 2 + 0x00, 0x00, 0x00, 0x0c, // length options array + 0x00, 0x09, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, // ip address + 0x00, 0x11, 0x77, 0x1a + }; + boost::asio::ip::address its_local_address = + boost::asio::ip::address::from_string(std::string(local_address)); + std::memcpy(&its_subscribe_message[64], &its_local_address.to_v4().to_bytes()[0], 4); + + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30490); + for (int var = 0; var < 15; ++var) { + udp_socket.send_to(boost::asio::buffer(its_subscribe_message), target_sd); + ++its_subscribe_message[11]; + } + + + if (std::future_status::timeout == all_offers_received.get_future().wait_for(std::chrono::seconds(10))) { + ADD_FAILURE() << "Didn't receive all Offers within time"; + } + + { + // call notify method (but don't expect notifications) to allow + // service to exit + std::uint8_t trigger_notifications_call[] = { + 0x11, 0x22, 0x42, 0x42, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + udp_socket.send_to(boost::asio::buffer(trigger_notifications_call), target_service); + } + + { + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x11, 0x22, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(remote_address)), + 30001); + udp_socket.send_to(boost::asio::buffer(shutdown_call), target_service); + } + + } catch (...) { + ASSERT_FALSE(true); + } + + }); + + send_thread.join(); + receive_thread.join(); + boost::system::error_code ec; + udp_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + udp_socket.close(ec); +} + #ifndef _WIN32 int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); @@ -1466,6 +1648,8 @@ int main(int argc, char** argv) { ::testing::GTEST_FLAG(filter) = "*subscribe_resubscribe_mixed"; } else if (its_testmode == std::string("SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE")) { ::testing::GTEST_FLAG(filter) = "*send_subscribe_stop_subscribe_subscribe"; + } else if (its_testmode == std::string("REQUEST_TO_SD")) { + ::testing::GTEST_FLAG(filter) = "*send_request_to_sd_port"; } return RUN_ALL_TESTS(); } diff --git a/test/pending_subscription_tests/pending_subscription_test_service.cpp b/test/pending_subscription_tests/pending_subscription_test_service.cpp index 2c33a44..6a917ea 100644 --- a/test/pending_subscription_tests/pending_subscription_test_service.cpp +++ b/test/pending_subscription_tests/pending_subscription_test_service.cpp @@ -133,24 +133,30 @@ public: << service_info_.service_id << "] Offering"; offer(); + if (testmode_ == pending_subscription_test::test_mode_e::REQUEST_TO_SD) { + // this testcase won't send valid subscriptions -> ensure to exit + subscription_accepted_asynchronous_ = true; + subscription_accepted_synchronous_ = true; + } + while (!subscription_accepted_asynchronous_ || !subscription_accepted_synchronous_) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - if (testmode_ == pending_subscription_test::test_mode_e::SUBSCRIBE) { - async_subscription_handler_(true); - } else if (testmode_ == pending_subscription_test::test_mode_e::SUBSCRIBE_UNSUBSCRIBE) { - ; - } else if (testmode_ == pending_subscription_test::test_mode_e::UNSUBSCRIBE) { - ; - } else if (testmode_ == pending_subscription_test::test_mode_e::SUBSCRIBE_UNSUBSCRIBE_NACK) { - ; - } else if (testmode_ == pending_subscription_test::test_mode_e::SUBSCRIBE_UNSUBSCRIBE_SAME_PORT) { - ; - } else if (testmode_ == pending_subscription_test::test_mode_e::SUBSCRIBE_RESUBSCRIBE_MIXED) { - ; - } else if (testmode_ == pending_subscription_test::test_mode_e::SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE) { - ; + switch (testmode_) { + case pending_subscription_test::test_mode_e::SUBSCRIBE: + async_subscription_handler_(true); + break; + case pending_subscription_test::test_mode_e::SUBSCRIBE_UNSUBSCRIBE: + case pending_subscription_test::test_mode_e::UNSUBSCRIBE: + case pending_subscription_test::test_mode_e::SUBSCRIBE_UNSUBSCRIBE_NACK: + case pending_subscription_test::test_mode_e::SUBSCRIBE_UNSUBSCRIBE_SAME_PORT: + case pending_subscription_test::test_mode_e::SUBSCRIBE_RESUBSCRIBE_MIXED: + case pending_subscription_test::test_mode_e::SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE: + case pending_subscription_test::test_mode_e::REQUEST_TO_SD: + default: + break; } + std::future<bool> itsFuture = notify_method_called_.get_future(); if (std::future_status::timeout == itsFuture.wait_for(std::chrono::seconds(10))) { ADD_FAILURE() << "notify method wasn't called within time!"; @@ -362,6 +368,8 @@ int main(int argc, char** argv) its_testmode = pending_subscription_test::test_mode_e::SUBSCRIBE_RESUBSCRIBE_MIXED; } else if (its_pased_testmode == std::string("SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE")) { its_testmode = pending_subscription_test::test_mode_e::SUBSCRIBE_STOPSUBSCRIBE_SUBSCRIBE; + } else if (its_pased_testmode == std::string("REQUEST_TO_SD")) { + its_testmode = pending_subscription_test::test_mode_e::REQUEST_TO_SD; } return RUN_ALL_TESTS(); diff --git a/test/security_config_plugin_tests/conf/security_config_plugin_test_local.json.in b/test/security_config_plugin_tests/conf/security_config_plugin_test_local.json.in new file mode 100644 index 0000000..a1497dc --- /dev/null +++ b/test/security_config_plugin_tests/conf/security_config_plugin_test_local.json.in @@ -0,0 +1,128 @@ +{ + "unicast":"127.0.0.1", + "logging": + { + "level":"debug", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "diagnosis" : "0x63", + "applications": + [ + { + "name":"vsomeipd", + "id" : "0x6310", + "plugins" : + [ + { + "application_plugin" : "vsomeip-security-config-plugin-mgu" + } + ] + } + ], + "security": { + "policies": [ + { + "credentials": { + "deny": [ + { + "uid": [{"first" : "@TEST_UID@", "last" : "@TEST_UID@"}], + "gid": [{"first" : "@TEST_GID@", "last" : "@TEST_GID@"}] + } + ] + }, + "deny": {} + }, + { + "credentials": { + "allow": [ + { + "uid": ["@TEST_UID@"], + "gid": ["@TEST_GID@"] + } + ] + }, + "allow" : + { + "offers": + [ + { + "service" : "0xF90F", + "instance" : "0x01" + }, + { + "service" : "0x0101", + "instance" : "0x63" + }, + { + "service" : "0x0103", + "instance" : "0x63" + } + ], + "requests": + [ + { + "service" : "0xF90F", + "instances" : + [ + { + "ids" : ["0x01"], + "methods" : [ "0x01", "0x02" ] + } + ] + }, + { + "service" : "0x0103", + "instances" : + [ + { + "ids" : ["0x63"], + "methods" : [ "0x03", "0x7777" ] + } + ] + }, + { + "service" : "0x0101", + "instances" : + [ + { + "ids" : ["0x63"], + "methods" : [ "0x88" ] + } + ] + } + ] + } + } + ], + "check_credentials" : "true" + }, + "security-update-whitelist" : + { + "uids" : + [ + "@TEST_UID@" + ], + "services" : + [ + {"first" : "0x101", "last" : "0x103"}, + "0xf90f" + ], + "check-whitelist" : "true" + }, + "routing":"vsomeipd", + "routing-credentials" : + { + "uid" : "@TEST_UID@", + "gid" : "@TEST_GID@" + }, + "service-discovery": + { + "enable":"false" + } +} diff --git a/test/security_config_plugin_tests/security_config_plugin_test_client.cpp b/test/security_config_plugin_tests/security_config_plugin_test_client.cpp new file mode 100755 index 0000000..9438738 --- /dev/null +++ b/test/security_config_plugin_tests/security_config_plugin_test_client.cpp @@ -0,0 +1,1045 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> +#include <cstring> + +#include <gtest/gtest.h> + +#ifndef _WIN32 +#include <signal.h> +#endif + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/utility/include/utility.hpp" +#include "../../implementation/logging/include/logger.hpp" +#include "../../implementation/configuration/include/policy.hpp" + +#include "../security_config_plugin_tests/security_config_plugin_test_globals.hpp" +class security_config_plugin_test_client; +static security_config_plugin_test_client* the_client; +extern "C" void signal_handler(int _signum); + +#define GET_LONG_BYTE0(x) ((x) & 0xFF) +#define GET_LONG_BYTE1(x) (((x) >> 8) & 0xFF) +#define GET_LONG_BYTE2(x) (((x) >> 16) & 0xFF) +#define GET_LONG_BYTE3(x) (((x) >> 24) & 0xFF) + +class security_config_plugin_test_client { +public: + security_config_plugin_test_client(bool _update_only, bool _remove_only, bool _subscribe_only) : + update_only_(_update_only), + remove_only_(_remove_only), + subscribe_only_(_subscribe_only), + app_(vsomeip::runtime::get()->create_application()), + wait_until_registered_(true), + wait_until_security_config_service_available_(true), + wait_until_control_service_available_(true), + wait_until_updated_service_101_available_(true), + wait_until_updated_service_102_available_(true), + wait_until_method_1_responses_received_(true), + wait_until_method_2_responses_received_(true), + number_of_received_responses_method_1(0), + number_of_received_responses_method_2(0), + number_of_received_events_1(0), + number_of_received_events_2(0), + number_of_received_events_4(0), + wait_until_notifications_1_received_(true), + wait_until_notifications_2_received_(true), + wait_for_stop_(true), + update_ok_(false), + removal_ok_(false), + stop_thread_(std::bind(&security_config_plugin_test_client::wait_for_stop, this)), + run_thread_(std::bind(&security_config_plugin_test_client::run, this)) { + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return; + } + + // register signal handler + the_client = this; + struct sigaction sa_new, sa_old; + sa_new.sa_handler = signal_handler; + sa_new.sa_flags = 0; + sigemptyset(&sa_new.sa_mask); + ::sigaction(SIGUSR1, &sa_new, &sa_old); + ::sigaction(SIGINT, &sa_new, &sa_old); + ::sigaction(SIGTERM, &sa_new, &sa_old); + ::sigaction(SIGABRT, &sa_new, &sa_old); + + app_->register_state_handler( + std::bind(&security_config_plugin_test_client::on_state, this, + std::placeholders::_1)); + + // handle plugin messages + app_->register_message_handler(security_config_plugin_test::security_config_plugin_serviceinfo.service_id, + security_config_plugin_test::security_config_plugin_serviceinfo.instance_id, + vsomeip::ANY_METHOD, + std::bind(&security_config_plugin_test_client::on_plugin_message, this, + std::placeholders::_1)); + + // handle service 0x0101 messages + app_->register_message_handler(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + vsomeip::ANY_METHOD, + std::bind(&security_config_plugin_test_client::on_service_1_message, this, + std::placeholders::_1)); + + // handle service 0x0102 messages + app_->register_message_handler(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + vsomeip::ANY_METHOD, + std::bind(&security_config_plugin_test_client::on_service_2_message, this, + std::placeholders::_1)); + + // request acl plugin interface service + app_->register_availability_handler( + security_config_plugin_test::security_config_plugin_serviceinfo.service_id, + security_config_plugin_test::security_config_plugin_serviceinfo.instance_id, + std::bind(&security_config_plugin_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3), + security_config_plugin_test::security_config_plugin_major_version_); + app_->request_service( + security_config_plugin_test::security_config_plugin_serviceinfo.service_id, + security_config_plugin_test::security_config_plugin_serviceinfo.instance_id, + security_config_plugin_test::security_config_plugin_major_version_, vsomeip::ANY_MINOR); + + // request control service which is globally allowed + app_->register_availability_handler( + security_config_plugin_test::security_config_test_serviceinfo_3.service_id, + security_config_plugin_test::security_config_test_serviceinfo_3.instance_id, + std::bind(&security_config_plugin_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service( + security_config_plugin_test::security_config_test_serviceinfo_3.service_id, + security_config_plugin_test::security_config_test_serviceinfo_3.instance_id, + vsomeip::ANY_MAJOR, vsomeip::ANY_MINOR); + + // request service 0x0101 which is globally blacklisted and shall get available after aclUpdate + app_->register_availability_handler( + security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + std::bind(&security_config_plugin_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service( + security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + vsomeip::ANY_MAJOR, vsomeip::ANY_MINOR); + + + // request service 0x0102 which is globally blacklisted and shall get available after aclUpdate + app_->register_availability_handler( + security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + std::bind(&security_config_plugin_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service( + security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + vsomeip::ANY_MAJOR, vsomeip::ANY_MINOR); + + // offer service 0x0100 which is not allowed to be offered at any time + // (log message "Skip offer!" should be printed by daemon) + app_->offer_service(0x666, security_config_plugin_test::security_config_test_serviceinfo_1.instance_id); + + app_->start(); + } + + ~security_config_plugin_test_client() { + run_thread_.join(); + stop_thread_.join(); + + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + if(_is_available) { + // check plugin interface availability + if (_service == security_config_plugin_test::security_config_plugin_serviceinfo.service_id && + _instance == security_config_plugin_test::security_config_plugin_serviceinfo.instance_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_security_config_service_available_ = false; + condition_.notify_one(); + } + + // check control service availability + if (_service == security_config_plugin_test::security_config_test_serviceinfo_3.service_id && + _instance == security_config_plugin_test::security_config_test_serviceinfo_3.instance_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_control_service_available_ = false; + condition_.notify_one(); + } + + // check updated policy makes service 0x0101 available + if (_service == security_config_plugin_test::security_config_test_serviceinfo_1.service_id && + _instance == security_config_plugin_test::security_config_test_serviceinfo_1.instance_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_updated_service_101_available_ = false; + condition_.notify_one(); + } + + // check updated policy makes service 0x0102 available + if (_service == security_config_plugin_test::security_config_test_serviceinfo_2.service_id && + _instance == security_config_plugin_test::security_config_test_serviceinfo_2.instance_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_updated_service_102_available_ = false; + condition_.notify_one(); + } + } else { + + } + } + + void on_service_1_message(const std::shared_ptr<vsomeip::message> &_message) { + if(_message->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) { + + number_of_received_events_1++; + + // event which should never be received + if (_message->get_method() == 0x8004) + number_of_received_events_4++; + + other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())]++; + VSOMEIP_INFO + << "on_service_1_message Received a notification with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method() <<"] (now have: " + << std::dec << other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())] << ")"; + + std::shared_ptr<vsomeip::payload> its_payload(_message->get_payload()); + EXPECT_EQ(2u, its_payload->get_length()); + EXPECT_EQ((_message->get_service() & 0xFF00 ) >> 8, its_payload->get_data()[0]); + EXPECT_EQ((_message->get_service() & 0xFF), its_payload->get_data()[1]); + bool notify = all_notifications_received(); + + if(notify) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_notifications_1_received_ = false; + condition_.notify_one(); + } + } else if (_message->get_message_type() == vsomeip::message_type_e::MT_RESPONSE) { + if(_message->get_method() == security_config_plugin_test::security_config_test_serviceinfo_1.method_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + number_of_received_responses_method_1++; + wait_until_method_1_responses_received_ = false; + condition_.notify_one(); + } + } + } + + void on_service_2_message(const std::shared_ptr<vsomeip::message> &_message) { + if(_message->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) { + + number_of_received_events_2++; + other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())]++; + VSOMEIP_INFO + << "on_service_2_message Received a notification with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method() <<"] (now have: " + << std::dec << other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())] << ")"; + + std::shared_ptr<vsomeip::payload> its_payload(_message->get_payload()); + EXPECT_EQ(2u, its_payload->get_length()); + EXPECT_EQ((_message->get_service() & 0xFF00 ) >> 8, its_payload->get_data()[0]); + EXPECT_EQ((_message->get_service() & 0xFF), its_payload->get_data()[1]); + bool notify = all_notifications_received(); + + if(notify) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_notifications_2_received_ = false; + condition_.notify_one(); + } + } else if (_message->get_message_type() == vsomeip::message_type_e::MT_RESPONSE) { + if(_message->get_method() == security_config_plugin_test::security_config_test_serviceinfo_2.method_id) { + std::lock_guard<std::mutex> its_lock(mutex_); + number_of_received_responses_method_2++; + wait_until_method_2_responses_received_ = false; + condition_.notify_one(); + } + } + } + + void on_plugin_message(const std::shared_ptr<vsomeip::message> &_message) { + if (_message->get_message_type() == vsomeip::message_type_e::MT_RESPONSE) { + VSOMEIP_INFO + << "Received a response with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method(); + + std::shared_ptr<vsomeip::payload> pl = _message->get_payload(); + EXPECT_EQ( vsomeip::return_code_e::E_OK, _message->get_return_code()); + + uint32_t length = pl->get_length(); + EXPECT_EQ(length, (uint32_t) 0x01); + + // expect status_success_ returned by plugin interface + bool success(false); + uint8_t* dataptr = pl->get_data(); + for(uint32_t i = 0; i< pl->get_length(); i++) { + std::cout << "payload data: " << std::hex << (uint32_t) dataptr[i] << std::endl; + EXPECT_EQ(dataptr[i], 0); + if (dataptr[i] == 0x00) { + success = true; + } + } + if (success) { + // updateAcl answer + if (_message->get_service() == security_config_plugin_test::security_config_plugin_serviceinfo.service_id + &&_message->get_method() == security_config_plugin_test::security_config_plugin_serviceinfo.method_id) { + update_ok_ = true; + } + + // removeAcl answer + if (_message->get_service() == security_config_plugin_test::security_config_plugin_serviceinfo.service_id + &&_message->get_method() == security_config_plugin_test::security_config_plugin_serviceinfo_reset.method_id) { + removal_ok_ = true; + } + } + } + } + + bool all_notifications_received() { + return std::all_of( + other_services_received_notification_.cbegin(), + other_services_received_notification_.cend(), + [&](const std::map<std::pair<vsomeip::service_t, + vsomeip::method_t>, std::uint32_t>::value_type& v) + { + if (v.second == security_config_plugin_test::notifications_to_send) { + return true; + } else { + if (v.second >= security_config_plugin_test::notifications_to_send) { + VSOMEIP_WARNING + << " Received multiple initial events from service/instance: " + << std::setw(4) << std::setfill('0') << std::hex << v.first.first + << "." + << std::setw(4) << std::setfill('0') << std::hex << v.first.second + << " number of received events: " << v.second + << ". This is caused by StopSubscribe/Subscribe messages and/or" + << " service offered via UDP and TCP"; + return false; + } else { + return false; + } + } + } + ); + } + + void handle_signal(int _signum) { + VSOMEIP_DEBUG << "Catched signal, going down (" + << std::dec <<_signum << ")"; + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + stop_condition_.notify_one(); + } + + void wait_for_stop() { + { + std::unique_lock<std::mutex> its_lock(stop_mutex_); + while (wait_for_stop_) { + stop_condition_.wait(its_lock); + } + + EXPECT_TRUE(update_ok_); + + // Todo if policy was removed, the plugin answer is not allowed to be sent back as plugin service is blacklisted again + //EXPECT_TRUE(removal_ok_); + + VSOMEIP_INFO << "Received all responses from plugin, going down"; + } + app_->clear_all_handler(); + app_->stop(); + } + + void call_acl_deployment_interface(bool _updateAcl) { + + uint32_t user_id = (uint32_t) getuid(); + uint32_t group_id = (uint32_t) getgid(); + + if(update_only_ || remove_only_) { + user_id = 1001; + group_id = 1001; + } + + uint8_t uid_byte_0 = (uint8_t) GET_LONG_BYTE3(user_id); + uint8_t uid_byte_1 = GET_LONG_BYTE2(user_id); + uint8_t uid_byte_2 = GET_LONG_BYTE1(user_id); + uint8_t uid_byte_3 = GET_LONG_BYTE0(user_id); + + uint8_t gid_byte_0 = (uint8_t) GET_LONG_BYTE3(group_id); + uint8_t gid_byte_1 = GET_LONG_BYTE2(group_id); + uint8_t gid_byte_2 = GET_LONG_BYTE1(group_id); + uint8_t gid_byte_3 = GET_LONG_BYTE0(group_id); + + // send policy payload which also contains part of global security json to allow answer for removeAcl calls + // updateAcl payload: + /* + Security configuration: UID: 0x3e8 + Security configuration: GID: 0x3e8 + Security configuration: RQUESTS POLICY SIZE: 4 + ALLOWED REQUESTS Service: 0x101 + Instances: + first: 0x63 last: 0x63 + Methods: + first: 0x1 last: 0x5 + first: 0x8001 last: 0x8003 + ALLOWED REQUESTS Service: 0x102 + Instances: + first: 0x63 last: 0x63 + Methods: + first: 0x1 last: 0x5 + first: 0x8001 last: 0x8003 + ALLOWED REQUESTS Service: 0x103 + Instances: + first: 0x63 last: 0x63 + Methods: + first: 0x3 last: 0x3 + first: 0x7777 last: 0x7777 + ALLOWED REQUESTS Service: 0xF90F + Instances: + first: 0x1 last: 0x1 + Methods: + first: 0x1 last: 0x1 + first: 0x2 last: 0x2 + Security configuration: OFFER POLICY SIZE: 4 + ALLOWED OFFERS Service: 0x101 + Instances: + first: 0x63 last: 0x63 + ALLOWED OFFERS Service: 0x102 + Instances: + first: 0x63 last: 0x63 + ALLOWED OFFERS Service: 0x103 + Instances: + first: 0x63 last: 0x63 + ALLOWED OFFERS Service: 0xF90F + Instances: + first: 0x1 last: 0x1 + */ + std::array<unsigned char, 280> update_payload = + {{ uid_byte_0, uid_byte_1, uid_byte_2, uid_byte_3, gid_byte_0, gid_byte_1, gid_byte_2, gid_byte_3, + 0x00, 0x00, 0x00, 0xb8, 0xF9, 0x0F, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x77, 0x77, 0x01, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x80, 0x01, 0x80, 0x03, 0x01, 0x02, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, + 0x80, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x40, 0xF9, 0x0F, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x01, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63}}; + + // removeAcl payload for UID 12345 GID 4567 or UID: 1000 -> 0x03, 0xe8, + std::array<unsigned char, 8> remove_payload = {{uid_byte_0, uid_byte_1, uid_byte_2, uid_byte_3, gid_byte_0, gid_byte_1, gid_byte_2, gid_byte_3}}; + + std::shared_ptr<vsomeip::message> its_request = vsomeip::runtime::get()->create_request(); + its_request->set_service(security_config_plugin_test::security_config_plugin_serviceinfo.service_id); + its_request->set_instance(security_config_plugin_test::security_config_plugin_serviceinfo.instance_id); + std::vector<vsomeip::byte_t> its_payload; + + if (_updateAcl) { + its_request->set_method(security_config_plugin_test::security_config_plugin_serviceinfo.method_id); + for (uint32_t i=0; i < update_payload.size(); i++) { + its_payload.push_back(update_payload[i]); + } + } else { + its_request->set_method(security_config_plugin_test::security_config_plugin_serviceinfo_reset.method_id); + for (uint32_t i=0; i < remove_payload.size(); i++) { + its_payload.push_back(remove_payload[i]); + } + } + its_request->set_payload(vsomeip::runtime::get()->create_payload(its_payload)); + app_->send(its_request); + } + + void call_try_offer() { + std::shared_ptr<vsomeip::message> its_request = vsomeip::runtime::get()->create_request(); + its_request->set_service(security_config_plugin_test::security_config_test_serviceinfo_3.service_id); + its_request->set_instance(security_config_plugin_test::security_config_test_serviceinfo_3.instance_id); + its_request->set_method(security_config_plugin_test::security_config_test_serviceinfo_3.method_id); + app_->send(its_request); + } + + void call_method(vsomeip::service_t _service, vsomeip::instance_t _instance, vsomeip::method_t _method) { + std::shared_ptr<vsomeip::message> its_request = vsomeip::runtime::get()->create_request(); + its_request->set_service(_service); + its_request->set_instance(_instance); + its_request->set_method(_method); + app_->send(its_request); + } + + void run() { + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + } + + // payload for testing utility::parse_policy() + /* + Security configuration: UID: 0x01 + Security configuration: GID: 0x01 + Security configuration: RQUESTS POLICY SIZE: 4 + ALLOWED REQUESTS Service: 0x101 + Instances: + first: 0x63 last: 0x63 + Methods: + first: 0x1 last: 0x5 + first: 0x8001 last: 0x8003 + ALLOWED REQUESTS Service: 0x102 + Instances: + first: 0x63 last: 0x63 + Methods: + first: 0x1 last: 0x5 + first: 0x8001 last: 0x8003 + ALLOWED REQUESTS Service: 0x103 + Instances: + first: 0x63 last: 0x63 + Methods: + first: 0x3 last: 0x3 + first: 0x7777 last: 0x7777 + ALLOWED REQUESTS Service: 0xF90F + Instances: + first: 0x1 last: 0x1 + Methods: + first: 0x1 last: 0x1 + first: 0x2 last: 0x2 + Security configuration: OFFER POLICY SIZE: 4 + ALLOWED OFFERS Service: 0x101 + Instances: + first: 0x63 last: 0x63 + ALLOWED OFFERS Service: 0x102 + Instances: + first: 0x63 last: 0x63 + ALLOWED OFFERS Service: 0x103 + Instances: + first: 0x63 last: 0x63 + ALLOWED OFFERS Service: 0xF90F + Instances: + first: 0x1 last: 0x1 + */ + std::array<unsigned char, 280> policy_update_payload = + {{ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, // uid / gid + 0x00, 0x00, 0x00, 0xb8, + 0xF9, 0x0F, // 0xf90f -> service ID at array index 12 - 13 + 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x02, // -> union length = 2 byte + 0x00, 0x00, 0x00, 0x01, // -> union type is single instance ID + 0x00, 0x01, // -> single instance id 0x1 at array index 30 - 31 + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x77, 0x77, 0x01, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x02, // -> union type is ID range + 0x00, 0x01, // first 0x01 at array index 132 - 133 + 0x00, 0x05, // last 0x05 at array index 134 - 135 + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x80, 0x01, 0x80, 0x03, 0x01, 0x02, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, + 0x80, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x40, 0xF9, 0x0F, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63, 0x01, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x63}}; + + // Check if parse_policy() correctly handles invalid service, instance and method ID's / ranges. + uint32_t its_uid = 0x00; + uint32_t its_gid = 0x00; + uint32_t its_remaining_bytes_ = policy_update_payload.size(); + std::shared_ptr<vsomeip::policy> its_policy(std::make_shared<vsomeip::policy>()); + const vsomeip::byte_t* its_buffer_ptr = policy_update_payload.data(); + + // valid policy + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), true); + + // set request service ID to invalid value 0x00 + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_service = policy_update_payload; + // set service ID from 0xf90f to 0x00 + policy_invalid_service[12] = 0x00; + policy_invalid_service[13] = 0x00; + its_buffer_ptr = policy_invalid_service.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), false); + + // set request service ID to invalid value 0xFFFF + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_service_2 = policy_update_payload; + // set service ID from 0xf90f to 0x00 + policy_invalid_service_2[12] = 0xFF; + policy_invalid_service_2[13] = 0xFF; + its_buffer_ptr = policy_invalid_service_2.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), false); + + // set request single instance ID to invalid value 0x00 which shall be ignored + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_instance = policy_update_payload; + // set instance ID of service 0xf90f to 0x00 + policy_invalid_instance[30] = 0x00; + policy_invalid_instance[31] = 0x00; + its_buffer_ptr = policy_invalid_instance.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), true); + + // set request single instance ID to valid value 0xFFFF + // meaning ANY_INSTANCE range from 0x01 to 0xFFFE + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_valid_instance = policy_update_payload; + // set instance ID of service 0xf90f to 0xFFFF + policy_valid_instance[30] = 0xFF; + policy_valid_instance[31] = 0xFF; + its_buffer_ptr = policy_valid_instance.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), true); + + // set first of range to 0x00 and last to 0x05 which is invalid + // the range shall be adjusted to be 0x01 to 0x05 without giving an error + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_method = policy_update_payload; + policy_invalid_method[132] = 0x00; + policy_invalid_method[133] = 0x00; + its_buffer_ptr = policy_invalid_method.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), true); + + // set first of range to 0x01 and last to 0x00 which is invalid + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_method_2 = policy_update_payload; + policy_invalid_method_2[134] = 0x00; + policy_invalid_method_2[135] = 0x00; + its_buffer_ptr = policy_invalid_method_2.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), false); + + // set first of range to 0x06 and last to 0x05 which is invalid + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_method_3 = policy_update_payload; + policy_invalid_method_3[132] = 0x00; + policy_invalid_method_3[133] = 0x06; + its_buffer_ptr = policy_invalid_method_3.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), false); + + // set first of range to 0x01 and last to 0xFFFF which is invalid as first and last + // must be set to 0xFFFF if ANY_METHOD / ANY_INSTANCE shall be specified + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_method_4 = policy_update_payload; + policy_invalid_method_4[134] = 0xFF; + policy_invalid_method_4[135] = 0xFF; + its_buffer_ptr = policy_invalid_method_4.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), false); + + // set first of range to 0xFFFF and last to 0xFFFF which is valid + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_valid_method = policy_update_payload; + policy_valid_method[132] = 0xFF; + policy_valid_method[133] = 0xFF; + policy_valid_method[134] = 0xFF; + policy_valid_method[135] = 0xFF; + its_buffer_ptr = policy_valid_method.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), true); + + // set first of range to 0x01 and last to 0x01 which is valid + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_valid_method_2 = policy_update_payload; + policy_valid_method_2[132] = 0x00; + policy_valid_method_2[133] = 0x01; + policy_valid_method_2[134] = 0x00; + policy_valid_method_2[135] = 0x01; + its_buffer_ptr = policy_valid_method_2.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), true); + + // set first of range to 0x01 and last to 0xFFFE which is valid + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_valid_method_3 = policy_update_payload; + policy_valid_method_3[132] = 0x00; + policy_valid_method_3[133] = 0x01; + policy_valid_method_3[134] = 0xFF; + policy_valid_method_3[135] = 0xFE; + its_buffer_ptr = policy_valid_method_3.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), true); + + // set first of range to 0xFFFF and last to 0x05 which is invalid + its_remaining_bytes_ = policy_update_payload.size(); + std::array<unsigned char, 280> policy_invalid_method_6 = policy_update_payload; + policy_invalid_method_6[132] = 0xFF; + policy_invalid_method_6[133] = 0xFF; + its_buffer_ptr = policy_invalid_method_6.data(); + EXPECT_EQ(::vsomeip::utility::parse_policy(its_buffer_ptr, its_remaining_bytes_, its_uid, its_gid, its_policy), false); + + VSOMEIP_INFO << "waiting until security_config plugin service is available!"; + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_security_config_service_available_) { + condition_.wait(its_lock); + } + wait_until_security_config_service_available_ = true; + VSOMEIP_INFO << "security config plugin service is available"; + } + + if (update_only_) { + VSOMEIP_INFO << "ONLY Calling updateAcl with user ID 1001 and exit -> shall allow method calls and events..."; + call_acl_deployment_interface(true); + sleep(1); + exit(0); + } else if (remove_only_) { + VSOMEIP_INFO << "ONLY Calling removeAcl with user ID 1001 and exit -> removes complete allow policy (disables all method calls / events)"; + call_acl_deployment_interface(false); + sleep(1); + exit(0); + } else if (subscribe_only_) { + VSOMEIP_INFO << "Subscribe to still blacklisted event 0x8004 and do not expect an event!"; + // check that events for service 0x101 and event 0x8004 are never received as policy does not allow it + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id); + app_->request_event(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8004), + its_eventgroups, true); + app_->subscribe(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id, + vsomeip::DEFAULT_MAJOR, vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE, + static_cast<vsomeip::event_t>(0x8004)); + sleep(1); + EXPECT_EQ(number_of_received_events_4, (uint32_t) 0); + exit(0); + } + + VSOMEIP_INFO << "Do method calls to blacklisted service/methods 0x101/0x01 and 0x102/0x02 and do not expect any response"; + + // do method calls to blacklisted services -> no response expected + call_method(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.method_id); + + call_method(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_2.method_id); + + + // check that no events are received before policy allows subscribing + VSOMEIP_INFO << "Subscribe to blacklisted service/event 0x101/0x8001 and do not expect any event"; + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id); + + app_->request_event(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(security_config_plugin_test::security_config_test_serviceinfo_1.event_id), + its_eventgroups, true); + app_->subscribe(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id, + vsomeip::DEFAULT_MAJOR, vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE, + static_cast<vsomeip::event_t>(security_config_plugin_test::security_config_test_serviceinfo_1.event_id)); + + sleep(1); + + EXPECT_EQ(number_of_received_responses_method_1, (uint32_t) 0); + EXPECT_EQ(number_of_received_responses_method_2, (uint32_t) 0); + EXPECT_EQ(number_of_received_events_1, (uint32_t) 0); + EXPECT_EQ(number_of_received_events_2, (uint32_t) 0); + + VSOMEIP_INFO << "Calling updateAcl which shall allow method calls and events..."; + call_acl_deployment_interface(true); + VSOMEIP_INFO << "wait until control service is available ..."; + + // wait until control service is available + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_control_service_available_) { + condition_.wait(its_lock); + } + wait_until_control_service_available_ = true; + VSOMEIP_INFO << "local control service 0x0103 available"; + } + // Todo wait until plugin has answered the acl update + sleep(1); + + // Send message_try_offer request in order to trigger stopoffer and reoffering of now allowed services + VSOMEIP_INFO << "trigger stopoffer / reoffer of service 0x0101 and 0x0102 ..."; + call_try_offer(); + + // try requesting the services 0x0101 and 0x0102 again + // (initial request service was rejected before which causes no routing info to be sent after policy update) + app_->request_service( + security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + vsomeip::ANY_MAJOR, vsomeip::ANY_MINOR); + + app_->request_service( + security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_3.instance_id, + vsomeip::ANY_MAJOR, vsomeip::ANY_MINOR); + + // check that services 0x0101 get available after acl update + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_updated_service_101_available_) { + condition_.wait(its_lock); + } + wait_until_updated_service_101_available_ = true; + VSOMEIP_INFO << "By policy update allowed service 0x0101 got available"; + } + + // check that services 0x0102 get available after acl update + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_updated_service_102_available_) { + condition_.wait(its_lock); + } + wait_until_updated_service_102_available_ = true; + VSOMEIP_INFO << "By policy update allowed service 0x0102 got available"; + } + + VSOMEIP_INFO << "Call now allowed service/methods 0x101/0x01 and 0x102/0x02 after allow policy update and expect responses!"; + + // call method ids which should now be allowed to be requested + call_method(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.method_id); + + // check that method calls are working + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_method_1_responses_received_) { + condition_.wait(its_lock); + } + wait_until_method_1_responses_received_ = true; + VSOMEIP_INFO << "By policy update allowed response to request of service/instance/method " + << std::hex << security_config_plugin_test::security_config_test_serviceinfo_1.service_id + << "/" << security_config_plugin_test::security_config_test_serviceinfo_1.instance_id + << "/" << security_config_plugin_test::security_config_test_serviceinfo_1.method_id << " received"; + } + + call_method(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_2.method_id); + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_method_2_responses_received_) { + condition_.wait(its_lock); + } + wait_until_method_2_responses_received_ = true; + VSOMEIP_INFO << "By policy update allowed response to request of service/instance/method " + << std::hex << security_config_plugin_test::security_config_test_serviceinfo_2.service_id + << "/" << security_config_plugin_test::security_config_test_serviceinfo_2.instance_id + << "/" << security_config_plugin_test::security_config_test_serviceinfo_2.method_id << " received"; + } + + EXPECT_EQ(number_of_received_responses_method_1, (uint32_t) 1); + EXPECT_EQ(number_of_received_responses_method_2, (uint32_t) 1); + + // check that events are received now + VSOMEIP_INFO << "Subscribe to service 0x101 / 0x102 events 0x8001 / 0x8002 and expect initial events"; + app_->request_event(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(security_config_plugin_test::security_config_test_serviceinfo_1.event_id), + its_eventgroups, true); + app_->subscribe(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id, + vsomeip::DEFAULT_MAJOR, vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE, + static_cast<vsomeip::event_t>(security_config_plugin_test::security_config_test_serviceinfo_1.event_id)); + + // check that from first service events were received + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_notifications_1_received_) { + condition_.wait(its_lock); + } + wait_until_notifications_1_received_ = true; + VSOMEIP_INFO << "notifications from service 0x0101 received"; + } + + app_->request_event(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + static_cast<vsomeip::event_t>(security_config_plugin_test::security_config_test_serviceinfo_2.event_id), + its_eventgroups, true); + app_->subscribe(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_2.eventgroup_id, + vsomeip::DEFAULT_MAJOR, vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE, + static_cast<vsomeip::event_t>(security_config_plugin_test::security_config_test_serviceinfo_2.event_id)); + + // check that from second service events were received + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_notifications_2_received_) { + condition_.wait(its_lock); + } + wait_until_notifications_2_received_ = true; + VSOMEIP_INFO << "notifications from service 0x0102 received"; + } + + VSOMEIP_INFO << "Subscribe to still blacklisted event 0x8004 and do not expect an event!"; + // check that events for service 0x101 and event 0x8004 are never received as policy does not allow it + app_->request_event(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8004), + its_eventgroups, true); + app_->subscribe(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id, + vsomeip::DEFAULT_MAJOR, vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE, + static_cast<vsomeip::event_t>(0x8004)); + sleep(1); + EXPECT_EQ(number_of_received_events_4, (uint32_t) 0); + + + // unsusbcribe + app_->unsubscribe(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id); + + app_->unsubscribe(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_2.eventgroup_id); + + // tigger removeAcl (removes the whole configuration for uid 1000) + VSOMEIP_INFO << "Calling removeAcl which removes complete allow policy (disables all method calls / events)"; + call_acl_deployment_interface(false); + + //Todo wait for plugin response + sleep(1); + + VSOMEIP_INFO << "Call now blacklisted methods after complete allow policy removal and do not expect any response!"; + // no response expected as policy was removed + call_method(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.method_id); + + call_method(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_2.method_id); + + // check that no more initial events are received now on resubscribe + VSOMEIP_INFO << "Subscribe to now blacklisted events after complete allow policy removal and do not expect initial events!"; + app_->subscribe(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id); + + app_->subscribe(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_2.eventgroup_id); + + sleep(1); + + EXPECT_EQ(number_of_received_responses_method_1, (uint32_t) 1); + EXPECT_EQ(number_of_received_responses_method_2, (uint32_t) 1); + EXPECT_EQ(number_of_received_events_1, (uint32_t) 1); + EXPECT_EQ(number_of_received_events_2, (uint32_t) 1); + + { + std::lock_guard<std::mutex> its_lock(stop_mutex_); + sleep(1); + wait_for_stop_ = false; + stop_condition_.notify_one(); + } + } + +private: + bool update_only_; + bool remove_only_; + bool subscribe_only_; + std::shared_ptr<vsomeip::application> app_; + bool wait_until_registered_; + bool wait_until_security_config_service_available_; + bool wait_until_control_service_available_; + bool wait_until_updated_service_101_available_; + bool wait_until_updated_service_102_available_; + bool wait_until_method_1_responses_received_; + bool wait_until_method_2_responses_received_; + uint32_t number_of_received_responses_method_1; + uint32_t number_of_received_responses_method_2; + uint32_t number_of_received_events_1; + uint32_t number_of_received_events_2; + uint32_t number_of_received_events_4; + bool wait_until_notifications_1_received_; + bool wait_until_notifications_2_received_; + std::mutex mutex_; + std::condition_variable condition_; + + bool wait_for_stop_; + bool update_ok_; + bool removal_ok_; + + std::mutex stop_mutex_; + std::condition_variable stop_condition_; + std::thread stop_thread_; + + std::thread run_thread_; + std::map<std::pair<vsomeip::service_t, vsomeip::method_t>, std::uint32_t> other_services_received_notification_; + +}; + +#if 1 +static bool only_update; +static bool only_remove; +static bool only_subscribe; +#endif + +extern "C" void signal_handler(int signum) { + the_client->handle_signal(signum); +} + +TEST(someip_security_config_plugin_test, update_remove_security_config_policy) +{ + security_config_plugin_test_client its_sample(only_update, only_remove, only_subscribe); +} + +#ifndef _WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); +#if 1 + only_update = false; + only_remove = false; + only_subscribe = false; + if (argc > 1) { + if(std::string("UPDATE") == std::string(argv[1])) { + only_update = true; + } else if(std::string("REMOVE") == std::string(argv[1])) { + only_remove = true; + } else if (std::string("SUBSCRIBE") == std::string(argv[1])) { + only_subscribe = true; + } + } + +#endif + + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/security_config_plugin_tests/security_config_plugin_test_globals.hpp b/test/security_config_plugin_tests/security_config_plugin_test_globals.hpp new file mode 100755 index 0000000..0e2f456 --- /dev/null +++ b/test/security_config_plugin_tests/security_config_plugin_test_globals.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef SECURITY_CONFIG_PLUGIN_TEST_GLOBALS_HPP_ +#define SECURITY_CONFIG_PLUGIN_TEST_GLOBALS_HPP_ + +namespace security_config_plugin_test { + +struct service_info { + vsomeip::service_t service_id; + vsomeip::instance_t instance_id; + vsomeip::method_t method_id; + vsomeip::event_t event_id; + vsomeip::eventgroup_t eventgroup_id; +}; + +// ACL interface of plugin (this service is allowed to be offered in global security config) +static constexpr service_info security_config_plugin_serviceinfo = { 0xF90F, 0x01, 0x1, 0x0, 0x0 }; +static constexpr vsomeip::major_version_t security_config_plugin_major_version_ = 0x01; +static constexpr vsomeip::minor_version_t security_config_plugin_minor_version_ = 0x00; + +static constexpr service_info security_config_plugin_serviceinfo_reset = { 0xF90F, 0x01, 0x2, 0x0, 0x0 }; + +// services to test policy (these services are denied to be offered in global security config and will be allowed via updateAcl) +static constexpr service_info security_config_test_serviceinfo_1 = { 0x0101, 0x63, 0x1, 0x8001, 0x1 }; +static constexpr service_info security_config_test_serviceinfo_2 = { 0x0102, 0x63, 0x2, 0x8002, 0x1 }; + +// service to control offering of above service instances via client method call (this service is allowed to be offered in global security config) +static constexpr service_info security_config_test_serviceinfo_3 = { 0x0103, 0x63, 0x3, 0x0, 0x0 }; + + +static constexpr int notifications_to_send = 1; +} + +#endif /* SECURITY_CONFIG_PLUGIN_TEST_GLOBALS_HPP_ */ diff --git a/test/security_config_plugin_tests/security_config_plugin_test_local_starter.sh b/test/security_config_plugin_tests/security_config_plugin_test_local_starter.sh new file mode 100755 index 0000000..dd1939c --- /dev/null +++ b/test/security_config_plugin_tests/security_config_plugin_test_local_starter.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +FAIL=0 + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Running first test +******************************************************************************* +******************************************************************************* +End-of-message + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=security_config_plugin_test_local.json + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(readlink -f ../plugins/mgu) +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +sleep 1 + +# Start the service +export VSOMEIP_APPLICATION_NAME=service-sample +./security_config_plugin_test_service & +PID_SERVICE=$! + +sleep 1 + +# Start the client +export VSOMEIP_APPLICATION_NAME=client-sample +./security_config_plugin_test_client & +CLIENT_PIDS+=($!) + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +kill $PID_VSOMEIPD +sleep 1 + +kill $PID_SERVICE +sleep 1 + +# Check if everything went well +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/security_config_plugin_tests/security_config_plugin_test_service.cpp b/test/security_config_plugin_tests/security_config_plugin_test_service.cpp new file mode 100644 index 0000000..40a7f8c --- /dev/null +++ b/test/security_config_plugin_tests/security_config_plugin_test_service.cpp @@ -0,0 +1,222 @@ +// Copyright (C) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "security_config_plugin_test_service.hpp" +#include "../security_config_plugin_tests/security_config_plugin_test_globals.hpp" + +security_config_plugin_test_service::security_config_plugin_test_service() : + app_(vsomeip::runtime::get()->create_application()), + is_registered_(false), + blocked_(false), + number_of_received_messages_(0), + offer_thread_(std::bind(&security_config_plugin_test_service::run, this)) { +} + +bool security_config_plugin_test_service::init() { + std::lock_guard<std::mutex> its_lock(mutex_); + + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return false; + } + app_->register_message_handler(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + vsomeip::ANY_METHOD, + std::bind(&security_config_plugin_test_service::on_message, this, + std::placeholders::_1)); + + app_->register_message_handler(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + vsomeip::ANY_METHOD, + std::bind(&security_config_plugin_test_service::on_message, this, + std::placeholders::_1)); + + app_->register_message_handler(security_config_plugin_test::security_config_test_serviceinfo_3.service_id, + security_config_plugin_test::security_config_test_serviceinfo_3.instance_id, + security_config_plugin_test::security_config_test_serviceinfo_3.method_id, + std::bind(&security_config_plugin_test_service::on_message_try_offer, this, + std::placeholders::_1)); + + app_->register_message_handler(security_config_plugin_test::security_config_test_serviceinfo_3.service_id, + security_config_plugin_test::security_config_test_serviceinfo_3.instance_id, + vsomeip_test::TEST_SERVICE_METHOD_ID_SHUTDOWN, + std::bind(&security_config_plugin_test_service::on_message_shutdown, this, + std::placeholders::_1)); + + app_->register_state_handler( + std::bind(&security_config_plugin_test_service::on_state, this, + std::placeholders::_1)); + + // offer allowed field 0x8001 eventgroup 0x01 + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(security_config_plugin_test::security_config_test_serviceinfo_1.eventgroup_id); + + app_->offer_event(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8001), its_eventgroups, true); + + // offer never allowed field 0x8004 eventgroup 0x01 + app_->offer_event(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8004), its_eventgroups, true); + + + // offer allowed field 0x8002 eventgroup 0x01 + app_->offer_event(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + static_cast<vsomeip::event_t>(0x8002), its_eventgroups, true); + + // set value to fields + std::shared_ptr<vsomeip::payload> its_payload = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data[2] = {static_cast<vsomeip::byte_t>((security_config_plugin_test::security_config_test_serviceinfo_1.service_id & 0xFF00) >> 8), + static_cast<vsomeip::byte_t>((security_config_plugin_test::security_config_test_serviceinfo_1.service_id & 0xFF))}; + its_payload->set_data(its_data, 2); + + app_->notify(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8001), its_payload); + + app_->notify(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8004), its_payload); + + app_->notify(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + static_cast<vsomeip::event_t>(0x8002), its_payload); + + return true; +} + +void security_config_plugin_test_service::start() { + VSOMEIP_INFO << "Starting..."; + app_->start(); +} + +void security_config_plugin_test_service::stop() { + VSOMEIP_INFO << "Stopping..."; + app_->clear_all_handler(); + app_->stop(); +} + +void security_config_plugin_test_service::join_offer_thread() { + if (offer_thread_.joinable()) { + offer_thread_.join(); + } +} + +void security_config_plugin_test_service::offer() { + // offer the initially in global security config file denied service / instance + app_->offer_service(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id); + + // offer the initially in global security config file denied service / instance + app_->offer_service(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id); + + // offer the allowed control service / instance + app_->offer_service(security_config_plugin_test::security_config_test_serviceinfo_3.service_id, + security_config_plugin_test::security_config_test_serviceinfo_3.instance_id); +} + +void security_config_plugin_test_service::stop_offer() { + app_->stop_offer_service(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, + security_config_plugin_test::security_config_test_serviceinfo_1.instance_id); + app_->stop_offer_service(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, + security_config_plugin_test::security_config_test_serviceinfo_2.instance_id); + app_->stop_offer_service(security_config_plugin_test::security_config_test_serviceinfo_3.service_id, + security_config_plugin_test::security_config_test_serviceinfo_3.instance_id); +} + +void security_config_plugin_test_service::on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? "registered." : + "deregistered."); + + if(_state == vsomeip::state_type_e::ST_REGISTERED) { + if(!is_registered_) { + is_registered_ = true; + std::lock_guard<std::mutex> its_lock(mutex_); + blocked_ = true; + // "start" the run method thread + condition_.notify_one(); + } + } + else { + is_registered_ = false; + } +} + +void security_config_plugin_test_service::on_message(const std::shared_ptr<vsomeip::message>& _request) { + VSOMEIP_INFO << "Received a message with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _request->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _request->get_session() << "] method: " << _request->get_method() ; + + // send response + std::shared_ptr<vsomeip::message> its_response = + vsomeip::runtime::get()->create_response(_request); + + app_->send(its_response, true); + + number_of_received_messages_++; + if(number_of_received_messages_ == vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND_SECURITY_TESTS) { + VSOMEIP_INFO << "Received all messages!"; + } +} + +void security_config_plugin_test_service::on_message_shutdown( + const std::shared_ptr<vsomeip::message>& _request) { + (void)_request; + VSOMEIP_INFO << "Shutdown method was called, going down now."; + stop(); +} + +void security_config_plugin_test_service::on_message_try_offer( + const std::shared_ptr<vsomeip::message>& _request) { + (void)_request; + VSOMEIP_INFO << "Try offering method was called."; + stop_offer(); + offer(); + + // set value to fields + std::shared_ptr<vsomeip::payload> its_payload = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data[2] = {static_cast<vsomeip::byte_t>((security_config_plugin_test::security_config_test_serviceinfo_1.service_id & 0xFF00) >> 8), + static_cast<vsomeip::byte_t>((security_config_plugin_test::security_config_test_serviceinfo_1.service_id & 0xFF))}; + its_payload->set_data(its_data, 2); + app_->notify(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8001), its_payload); + + app_->notify(security_config_plugin_test::security_config_test_serviceinfo_1.service_id, security_config_plugin_test::security_config_test_serviceinfo_1.instance_id, + static_cast<vsomeip::event_t>(0x8004), its_payload); + + // set value to fields + std::shared_ptr<vsomeip::payload> its_payload_2 = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data_2[2] = {static_cast<vsomeip::byte_t>((security_config_plugin_test::security_config_test_serviceinfo_2.service_id & 0xFF00) >> 8), + static_cast<vsomeip::byte_t>((security_config_plugin_test::security_config_test_serviceinfo_2.service_id & 0xFF))}; + its_payload_2->set_data(its_data_2, 2); + app_->notify(security_config_plugin_test::security_config_test_serviceinfo_2.service_id, security_config_plugin_test::security_config_test_serviceinfo_2.instance_id, + static_cast<vsomeip::event_t>(0x8002), its_payload_2); + +} + +void security_config_plugin_test_service::run() { + std::unique_lock<std::mutex> its_lock(mutex_); + while (!blocked_) + condition_.wait(its_lock); + + offer(); +} + +TEST(someip_security_test, basic_security_update_) { + security_config_plugin_test_service test_service; + if (test_service.init()) { + test_service.start(); + test_service.join_offer_thread(); + } +} + +#ifndef _WIN32 +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/security_config_plugin_tests/security_config_plugin_test_service.hpp b/test/security_config_plugin_tests/security_config_plugin_test_service.hpp new file mode 100644 index 0000000..eb43957 --- /dev/null +++ b/test/security_config_plugin_tests/security_config_plugin_test_service.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef SECURITY_CONFIG_PLUGIN_TEST_SERVICE_HPP +#define SECURITY_CONFIG_PLUGIN_TEST_SERVICE_HPP + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> + +#include "../someip_test_globals.hpp" + +#include <thread> +#include <mutex> +#include <condition_variable> + +class security_config_plugin_test_service { +public: + security_config_plugin_test_service(); + bool init(); + void start(); + void stop(); + void offer(); + void stop_offer(); + void join_offer_thread(); + void on_state(vsomeip::state_type_e _state); + void on_message(const std::shared_ptr<vsomeip::message> &_request); + void on_message_shutdown(const std::shared_ptr<vsomeip::message> &_request); + void on_message_try_offer(const std::shared_ptr<vsomeip::message> &_request); + void run(); + +private: + std::shared_ptr<vsomeip::application> app_; + bool is_registered_; + + std::mutex mutex_; + std::condition_variable condition_; + bool blocked_; + std::uint32_t number_of_received_messages_; + std::thread offer_thread_; +}; + +#endif // SECURITY_CONFIG_PLUGIN_TEST_SERVICE_HPP diff --git a/test/security_tests/conf/security_test_config_client_external_allow.json.in b/test/security_tests/conf/security_test_config_client_external_allow.json.in new file mode 100644 index 0000000..531c620 --- /dev/null +++ b/test/security_tests/conf/security_test_config_client_external_allow.json.in @@ -0,0 +1,90 @@ +{ + "unicast" : "@TEST_IP_MASTER@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "client-sample", + "id" : "0x1255" + }, + { + "name" : "vsomeipd", + "id" : "0x2222" + } + ], + "services" : + [ + { + "service" : "0x1234", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x111", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x1234", + "instance" : "0x02", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + } + ], + "security" : + { + "check_credentials" : "true", + "allow_remote_clients" : "true", + "policies" : + [ + { + "credentials" : { "uid" : "@TEST_UID@", "gid" : "@TEST_GID@" }, + "allow" : + { + "requests": + [ + { + "service" : "0x1234", + "instances" : + [ + { + "ids" : ["0x5678"], + "methods" : [ {"first" : "0x8421", "last" : "0x8422" }, "0x8001", "0x7777" ] + } + ] + } + ] + } + } + ] + }, + "routing" : "vsomeipd", + "routing-credentials" : + { + "uid" : "@TEST_UID@", + "gid" : "@TEST_GID@" + }, + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "2000", + "request_response_delay" : "1500" + } +} diff --git a/test/security_tests/conf/security_test_config_client_external_deny.json.in b/test/security_tests/conf/security_test_config_client_external_deny.json.in new file mode 100644 index 0000000..0cd96fb --- /dev/null +++ b/test/security_tests/conf/security_test_config_client_external_deny.json.in @@ -0,0 +1,90 @@ +{ + "unicast" : "@TEST_IP_MASTER@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "client-sample", + "id" : "0x1255" + }, + { + "name" : "vsomeipd", + "id" : "0x2222" + } + ], + "services" : + [ + { + "service" : "0x1234", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x111", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x1234", + "instance" : "0x02", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + } + ], + "security" : + { + "check_credentials" : "true", + "allow_remote_clients" : "false", + "policies" : + [ + { + "credentials" : { "uid" : "@TEST_UID@", "gid" : "@TEST_GID@" }, + "allow" : + { + "requests": + [ + { + "service" : "0x1234", + "instances" : + [ + { + "ids" : ["0x5678"], + "methods" : [ {"first" : "0x8421", "last" : "0x8422" }, "0x8001", "0x7777" ] + } + ] + } + ] + } + } + ] + }, + "routing" : "vsomeipd", + "routing-credentials" : + { + "uid" : "@TEST_UID@", + "gid" : "@TEST_GID@" + }, + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "2000", + "request_response_delay" : "1500" + } +} diff --git a/test/security_tests/conf/security_test_config_service_external_allow.json.in b/test/security_tests/conf/security_test_config_service_external_allow.json.in new file mode 100644 index 0000000..e95b942 --- /dev/null +++ b/test/security_tests/conf/security_test_config_service_external_allow.json.in @@ -0,0 +1,84 @@ +{ + "unicast" : "@TEST_IP_SLAVE@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "service-sample", + "id" : "0x1277" + }, + { + "name" : "vsomeipd", + "id" : "0x1111" + } + ], + "services" : + [ + { + "service" : "0x1234", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x111", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x1234", + "instance" : "0x02", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + } + ], + "security" : + { + "check_credentials" : "true", + "allow_remote_clients" : "true", + "policies" : + [ + { + "credentials" : { "uid" : "@TEST_UID@", "gid" : "@TEST_GID@" }, + "allow" : + { + "offers": + [ + { + "service" : "0x1234", + "instance" : "0x5678" + } + ] + } + } + ] + }, + "routing" : "vsomeipd", + "routing-credentials" : + { + "uid" : "@TEST_UID@", + "gid" : "@TEST_GID@" + }, + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "2000", + "request_response_delay" : "1500" + } +} diff --git a/test/security_tests/conf/security_test_config_service_external_deny.json.in b/test/security_tests/conf/security_test_config_service_external_deny.json.in new file mode 100644 index 0000000..8caf0af --- /dev/null +++ b/test/security_tests/conf/security_test_config_service_external_deny.json.in @@ -0,0 +1,84 @@ +{ + "unicast" : "@TEST_IP_SLAVE@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "service-sample", + "id" : "0x1277" + }, + { + "name" : "vsomeipd", + "id" : "0x1111" + } + ], + "services" : + [ + { + "service" : "0x1234", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x111", + "instance" : "0x5678", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + }, + { + "service" : "0x1234", + "instance" : "0x02", + "unicast" : "@TEST_IP_MASTER@", + "unreliable" : "30509" + } + ], + "security" : + { + "check_credentials" : "true", + "allow_remote_clients" : "false", + "policies" : + [ + { + "credentials" : { "uid" : "@TEST_UID@", "gid" : "@TEST_GID@" }, + "allow" : + { + "offers": + [ + { + "service" : "0x1234", + "instance" : "0x5678" + } + ] + } + } + ] + }, + "routing" : "vsomeipd", + "routing-credentials" : + { + "uid" : "@TEST_UID@", + "gid" : "@TEST_GID@" + }, + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "2000", + "request_response_delay" : "1500" + } +} diff --git a/test/security_tests/conf/security_test_config.json.in b/test/security_tests/conf/security_test_local_config.json.in index 693bf58..aab7f39 100644 --- a/test/security_tests/conf/security_test_config.json.in +++ b/test/security_tests/conf/security_test_local_config.json.in @@ -16,6 +16,10 @@ { "name" : "client-sample", "id" : "0x1255" + }, + { + "name" : "vsomeipd", + "id" : "0x1111" } ], "security" : @@ -24,7 +28,6 @@ "policies" : [ { - "client" : "0x1277", "credentials" : { "uid" : "@TEST_UID@", "gid" : "@TEST_GID@" }, "allow" : { @@ -34,26 +37,30 @@ "service" : "0x1234", "instance" : "0x5678" } - ] - } - }, - { - "client" : "0x1255", - "credentials" : { "uid" : "@TEST_UID@", "gid" : "@TEST_GID@" }, - "allow" : - { + ], "requests": [ { "service" : "0x1234", - "instance" : "0x5678" + "instances" : + [ + { + "ids" : ["0x5678"], + "methods" : [ {"first" : "0x8421", "last" : "0x8422" }, "0x8001", "0x7777" ] + } + ] } ] } } ] }, - "routing" : "service-sample", + "routing" : "vsomeipd", + "routing-credentials" : + { + "uid" : "@TEST_UID@", + "gid" : "@TEST_GID@" + }, "service-discovery" : { "enable" : "true", diff --git a/test/security_tests/security_test_client.cpp b/test/security_tests/security_test_client.cpp index 924f13c..2841f23 100644 --- a/test/security_tests/security_test_client.cpp +++ b/test/security_tests/security_test_client.cpp @@ -5,11 +5,18 @@ #include "security_test_client.hpp" -security_test_client::security_test_client() +static bool is_remote_test = false; +static bool remote_client_allowed = true; + +security_test_client::security_test_client(bool _test_external_communication, + bool _is_remote_client_allowed) : app_(vsomeip::runtime::get()->create_application()), is_available_(false), sender_(std::bind(&security_test_client::run, this)), - received_responses_(0) { + received_responses_(0), + received_allowed_events_(0), + test_external_communication_(_test_external_communication), + is_remote_client_allowed_(_is_remote_client_allowed) { } @@ -33,6 +40,18 @@ bool security_test_client::init() { std::bind(&security_test_client::on_availability, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + + app_->register_availability_handler(0x111, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, + std::bind(&security_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + + app_->register_availability_handler(vsomeip_test::TEST_SERVICE_SERVICE_ID, + 0x02, + std::bind(&security_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); return true; } @@ -45,7 +64,9 @@ void security_test_client::start() { void security_test_client::stop() { VSOMEIP_INFO << "Stopping..."; - shutdown_service(); + if (is_remote_client_allowed_) { + shutdown_service(); + } std::this_thread::sleep_for(std::chrono::milliseconds(100)); @@ -57,6 +78,32 @@ void security_test_client::on_state(vsomeip::state_type_e _state) { if(_state == vsomeip::state_type_e::ST_REGISTERED) { app_->request_service(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, false); + + // request not allowed service ID + app_->request_service(0x111, + vsomeip_test::TEST_SERVICE_INSTANCE_ID, false); + + // request not allowed instance ID + app_->request_service(vsomeip_test::TEST_SERVICE_SERVICE_ID, + 0x02, false); + + // request events of eventgroup 0x01 which holds events 0x8001 (allowed) and 0x8002 (denied) + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(0x01); + app_->request_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), + its_eventgroups, true); + app_->request_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8002), + its_eventgroups, true); + + app_->subscribe(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, 0x01, + vsomeip::DEFAULT_MAJOR, vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE, + static_cast<vsomeip::event_t>(0x8001)); + + app_->subscribe(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, 0x01, + vsomeip::DEFAULT_MAJOR, vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE, + static_cast<vsomeip::event_t>(0x8002)); } } @@ -68,6 +115,12 @@ void security_test_client::on_availability(vsomeip::service_t _service, << _service << "." << _instance << "] is " << (_is_available ? "available." : "NOT available."); + // check that only the allowed service / instance ID gets available + if (_is_available) { + EXPECT_EQ(vsomeip_test::TEST_SERVICE_SERVICE_ID, _service); + EXPECT_EQ(vsomeip_test::TEST_SERVICE_INSTANCE_ID, _instance); + } + if(vsomeip_test::TEST_SERVICE_SERVICE_ID == _service && vsomeip_test::TEST_SERVICE_INSTANCE_ID == _instance) { std::unique_lock<std::mutex> its_lock(mutex_); @@ -92,13 +145,26 @@ void security_test_client::on_message(const std::shared_ptr<vsomeip::message> &_ << std::setw(4) << std::setfill('0') << std::hex << _response->get_session() << "]"; - if (_response->get_service() == vsomeip_test::TEST_SERVICE_SERVICE_ID && - _response->get_instance() == vsomeip_test::TEST_SERVICE_INSTANCE_ID) { - received_responses_++; - if (received_responses_ == vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND_SECURITY_TESTS) { - VSOMEIP_WARNING << std::hex << app_->get_client() - << ": Received all messages ~> going down!"; + if(_response->get_message_type() == vsomeip::message_type_e::MT_RESPONSE) { + EXPECT_EQ(vsomeip_test::TEST_SERVICE_SERVICE_ID, _response->get_service()); + EXPECT_EQ(vsomeip_test::TEST_SERVICE_INSTANCE_ID, _response->get_instance()); + EXPECT_EQ(vsomeip_test::TEST_SERVICE_METHOD_ID, _response->get_method()); + + if (_response->get_service() == vsomeip_test::TEST_SERVICE_SERVICE_ID && + _response->get_instance() == vsomeip_test::TEST_SERVICE_INSTANCE_ID && + _response->get_method() == vsomeip_test::TEST_SERVICE_METHOD_ID) { + received_responses_++; + if (received_responses_ == vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND_SECURITY_TESTS) { + VSOMEIP_WARNING << std::hex << app_->get_client() + << ": Received all messages ~> going down!"; + } } + } else if (_response->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) { + // check that only allowed event 0x8001 is received + EXPECT_EQ(vsomeip_test::TEST_SERVICE_SERVICE_ID, _response->get_service()); + EXPECT_EQ(vsomeip_test::TEST_SERVICE_INSTANCE_ID, _response->get_instance()); + EXPECT_EQ(0x8001, _response->get_method()); + received_allowed_events_++; } } @@ -116,15 +182,31 @@ void security_test_client::run() { request->set_service(vsomeip_test::TEST_SERVICE_SERVICE_ID); request->set_instance(vsomeip_test::TEST_SERVICE_INSTANCE_ID); request->set_method(vsomeip_test::TEST_SERVICE_METHOD_ID); + + // send a request which is allowed by policy -> expect answer + app_->send(request, true); + + // send a request with a not allowed method ID -> expect no answer + request->set_method(0x888); app_->send(request, true); std::this_thread::sleep_for(std::chrono::milliseconds(250)); } std::this_thread::sleep_for(std::chrono::milliseconds(250)); - EXPECT_EQ(vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND_SECURITY_TESTS, - received_responses_); + if (!test_external_communication_) { + EXPECT_EQ(vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND_SECURITY_TESTS, + received_responses_); + EXPECT_EQ(received_allowed_events_, (uint32_t) 0x01); + } else if (test_external_communication_ && !is_remote_client_allowed_) { + EXPECT_EQ((uint32_t)0, received_responses_); + EXPECT_EQ((uint32_t)0, received_allowed_events_); + } else if (test_external_communication_ && is_remote_client_allowed_) { + EXPECT_EQ(vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND_SECURITY_TESTS, + received_responses_); + EXPECT_EQ(received_allowed_events_, (uint32_t) 0x01); + } stop(); } @@ -143,9 +225,9 @@ void security_test_client::shutdown_service() { app_->send(request,true); } -TEST(someip_security_test, basic_request_response) +TEST(someip_security_test, basic_subscribe_request_response) { - security_test_client test_client; + security_test_client test_client(is_remote_test, remote_client_allowed); if (test_client.init()) { test_client.start(); test_client.join_sender_thread(); @@ -153,6 +235,44 @@ TEST(someip_security_test, basic_request_response) } int main(int argc, char** argv) { + + std::string test_remote("--remote"); + std::string test_local("--local"); + std::string test_allow_remote_client("--allow"); + std::string test_deny_remote_client("--deny"); + std::string help("--help"); + + int i = 1; + while (i < argc) + { + if(test_remote == argv[i]) + { + is_remote_test = true; + } + else if(test_local == argv[i]) + { + is_remote_test = false; + } + else if(test_allow_remote_client == argv[i]) + { + remote_client_allowed = true; + } + else if(test_deny_remote_client == argv[i]) + { + remote_client_allowed = false; + } + else if(help == argv[i]) + { + VSOMEIP_INFO << "Parameters:\n" + << "--remote: Run test between two hosts\n" + << "--local: Run test locally\n" + << "--allow: test is started with a policy that allows remote messages sent by this test client to the service\n" + << "--deny: test is started with a policy that denies remote messages sent by this test client to the service\n" + << "--help: print this help"; + } + i++; + } + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -}
\ No newline at end of file +} diff --git a/test/security_tests/security_test_client.hpp b/test/security_tests/security_test_client.hpp index ed010fa..df2acc8 100644 --- a/test/security_tests/security_test_client.hpp +++ b/test/security_tests/security_test_client.hpp @@ -20,7 +20,8 @@ class security_test_client { public: - security_test_client(); + security_test_client(bool _test_external_communication, + bool _is_remote_client_allowed); bool init(); void start(); void stop(); @@ -45,6 +46,10 @@ private: std::thread sender_; std::atomic<std::uint32_t> received_responses_; + std::atomic<std::uint32_t> received_allowed_events_; + + bool test_external_communication_; + bool is_remote_client_allowed_; }; #endif // SECURITY_TEST_CLIENT_HPP diff --git a/test/security_tests/security_test_client_start.sh b/test/security_tests/security_test_client_start.sh deleted file mode 100755 index e87ca31..0000000 --- a/test/security_tests/security_test_client_start.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -export VSOMEIP_APPLICATION_NAME=client-sample -export VSOMEIP_CONFIGURATION=vsomeip-security.json -./security_test_client diff --git a/test/security_tests/security_test_external_master_start.sh b/test/security_tests/security_test_external_master_start.sh new file mode 100755 index 0000000..855654b --- /dev/null +++ b/test/security_tests/security_test_external_master_start.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 2 ] +then + echo "Please pass a json file to this script and wether remote clients are allowed or not " + echo "For example: $0 security_test_config_client_external_allow.json --allow" + exit 1 +fi + +MASTER_JSON_FILE=$1 +SERVICE_JSON_FILE=${MASTER_JSON_FILE/client/service} +ALLOW_DENY=$2 + +FAIL=0 + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=vsomeipd +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=client-sample +./security_test_client --remote $2 & +PID_CLIENT=$! + + +if [ ! -z "$USE_LXC_TEST" ]; then + echo "starting external security test on slave LXC" + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip/test; ./security_test_external_slave_start.sh $SERVICE_JSON_FILE $2\"" & +elif [ ! -z "$USE_DOCKER" ]; then + docker run --name citms --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && ./security_test_external_slave_start.sh $SERVICE_JSON_FILE $2" & +else +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** security_test_external_slave_start.sh $SERVICE_JSON_FILE $2 +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** security_test_config_service_external_allow.json and +** security_test_config_client_external_allow.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message +fi + +# Wait until client and service are finished +for client_pid in "${PID_CLIENT}" +do + if [ -n "$client_pid" ]; then + # Fail gets incremented if either client or service exit + # with a non-zero exit code + wait "$client_pid" || ((FAIL+=1)) + fi +done + +if [ ! -z "$USE_DOCKER" ]; then + docker stop citms + docker rm citms +fi + +kill $PID_VSOMEIPD +kill $PID_CLIENT + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/security_tests/security_test_external_slave_start.sh b/test/security_tests/security_test_external_slave_start.sh new file mode 100755 index 0000000..2b2391f --- /dev/null +++ b/test/security_tests/security_test_external_slave_start.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 2 ] +then + echo "Please pass a json file to this script and wether remote clients are allowed or not " + echo "For example: $0 security_test_config_service_external_allow.json --allow" + exit 1 +fi + +CLIENT_JSON_FILE=$1 +ALLOW_DENY=$2 + +FAIL=0 + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=vsomeipd +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=service-sample +./security_test_service --remote $2 & +PID_SERVICE=$! + +# Wait until client and service are finished +for client_pid in "${PID_SERVICE}" +do + if [ -n "$client_pid" ]; then + # Fail gets incremented if either client or service exit + # with a non-zero exit code + wait "$client_pid" || ((FAIL+=1)) + fi +done + +if [ ! -z "$USE_DOCKER" ]; then + docker stop citms + docker rm citms +fi + +kill $PID_VSOMEIPD +kill $PID_SERVICE + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/security_tests/security_test_start.sh b/test/security_tests/security_test_local_start.sh index ac4f51f..886b412 100755 --- a/test/security_tests/security_test_start.sh +++ b/test/security_tests/security_test_local_start.sh @@ -4,12 +4,22 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -export VSOMEIP_CONFIGURATION=security_test_config.json +export VSOMEIP_CONFIGURATION=security_test_local_config.json + +export VSOMEIP_APPLICATION_NAME=vsomeipd +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +sleep 1 export VSOMEIP_APPLICATION_NAME=service-sample -./security_test_service & +./security_test_service --local & sleep 1 export VSOMEIP_APPLICATION_NAME=client-sample -./security_test_client +./security_test_client --local + +kill $PID_VSOMEIPD +sleep 1 diff --git a/test/security_tests/security_test_service.cpp b/test/security_tests/security_test_service.cpp index 99fb14e..baaa60d 100644 --- a/test/security_tests/security_test_service.cpp +++ b/test/security_tests/security_test_service.cpp @@ -5,6 +5,9 @@ #include "security_test_service.hpp" +static bool is_remote_test = false; +static bool remote_client_allowed = true; + security_test_service::security_test_service() : app_(vsomeip::runtime::get()->create_application()), is_registered_(false), @@ -34,6 +37,31 @@ bool security_test_service::init() { app_->register_state_handler( std::bind(&security_test_service::on_state, this, std::placeholders::_1)); + + // offer allowed field 0x8001 eventgroup 0x01 + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(0x01); + + app_->offer_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), its_eventgroups, true); + + // also offer field 0x8002 which is not allowed to be received by client + app_->offer_event(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8002), its_eventgroups, true); + + // set value to fields + std::shared_ptr<vsomeip::payload> its_payload = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data[2] = {static_cast<vsomeip::byte_t>((vsomeip_test::TEST_SERVICE_SERVICE_ID & 0xFF00) >> 8), + static_cast<vsomeip::byte_t>((vsomeip_test::TEST_SERVICE_SERVICE_ID & 0xFF))}; + its_payload->set_data(its_data, 2); + + app_->notify(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), its_payload); + + app_->notify(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8002), its_payload); + return true; } @@ -56,6 +84,12 @@ void security_test_service::join_offer_thread() { void security_test_service::offer() { app_->offer_service(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID); + + // try to offer a not allowed instance ID 0x02 (client requesting the service should not get available) + app_->offer_service(vsomeip_test::TEST_SERVICE_SERVICE_ID, 0x02); + + // try to offer a not allowed service ID 0x111 (client requesting the service should not get available) + app_->offer_service(0x111, vsomeip_test::TEST_SERVICE_INSTANCE_ID); } void security_test_service::stop_offer() { @@ -83,12 +117,12 @@ void security_test_service::on_state(vsomeip::state_type_e _state) { void security_test_service::on_message(const std::shared_ptr<vsomeip::message>& _request) { ASSERT_EQ(vsomeip_test::TEST_SERVICE_SERVICE_ID, _request->get_service()); - ASSERT_EQ(vsomeip_test::TEST_SERVICE_METHOD_ID, _request->get_method()); + ASSERT_EQ(vsomeip_test::TEST_SERVICE_INSTANCE_ID, _request->get_instance()); VSOMEIP_INFO << "Received a message with Client/Session [" << std::setw(4) << std::setfill('0') << std::hex << _request->get_client() << "/" << std::setw(4) << std::setfill('0') << std::hex - << _request->get_session() << "]"; + << _request->get_session() << "] method: " << _request->get_method() ; // send response std::shared_ptr<vsomeip::message> its_response = @@ -115,9 +149,17 @@ void security_test_service::run() { condition_.wait(its_lock); offer(); + + // do not wait for the shutdown method to be called + if (is_remote_test && !remote_client_allowed) { + std::this_thread::sleep_for(std::chrono::milliseconds(250 * vsomeip_test::NUMBER_OF_MESSAGES_TO_SEND_SECURITY_TESTS + 10000)); + VSOMEIP_INFO << "Shutdown the service after timeout as remote client is not allowed by policy to call shutdown method!"; + stop(); + } + } -TEST(someip_security_test, basic_request_response) { +TEST(someip_security_test, basic_subscribe_request_response) { security_test_service test_service; if (test_service.init()) { test_service.start(); @@ -127,6 +169,44 @@ TEST(someip_security_test, basic_request_response) { #ifndef _WIN32 int main(int argc, char** argv) { + + std::string test_remote("--remote"); + std::string test_local("--local"); + std::string test_allow_remote_client("--allow"); + std::string test_deny_remote_client("--deny"); + std::string help("--help"); + + int i = 1; + while (i < argc) + { + if(test_remote == argv[i]) + { + is_remote_test = true; + } + else if(test_local == argv[i]) + { + is_remote_test = false; + } + else if(test_allow_remote_client == argv[i]) + { + remote_client_allowed = true; + } + else if(test_deny_remote_client == argv[i]) + { + remote_client_allowed = false; + } + else if(help == argv[i]) + { + VSOMEIP_INFO << "Parameters:\n" + << "--remote: Run test between two hosts\n" + << "--local: Run test locally\n" + << "--allow: test is started with a policy that allows remote messages sent by this test client to the service\n" + << "--deny: test is started with a policy that denies remote messages sent by this test client to the service\n" + << "--help: print this help"; + } + i++; + } + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp b/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp index 73cfbed..f738b38 100644 --- a/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp +++ b/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp @@ -86,7 +86,7 @@ public: auto handler = std::bind(&subscribe_notify_one_test_service::on_subscription_state_change, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5); - app_->register_subscription_status_handler(i.service_id, i.instance_id, i.eventgroup_id, vsomeip::ANY_EVENT, handler); + app_->register_subscription_status_handler(i.service_id, i.instance_id, i.eventgroup_id, vsomeip::ANY_EVENT, handler, true); app_->register_subscription_status_handler(vsomeip::ANY_SERVICE, i.instance_id, i.eventgroup_id, vsomeip::ANY_EVENT, handler); app_->register_subscription_status_handler(i.service_id, vsomeip::ANY_INSTANCE, i.eventgroup_id, vsomeip::ANY_EVENT, handler); app_->register_subscription_status_handler(vsomeip::ANY_SERVICE, vsomeip::ANY_INSTANCE, i.eventgroup_id, vsomeip::ANY_EVENT, handler); diff --git a/test/subscribe_notify_tests/subscribe_notify_test_one_event_two_eventgroups_client.cpp b/test/subscribe_notify_tests/subscribe_notify_test_one_event_two_eventgroups_client.cpp index 0b88e25..8d9f856 100644 --- a/test/subscribe_notify_tests/subscribe_notify_test_one_event_two_eventgroups_client.cpp +++ b/test/subscribe_notify_tests/subscribe_notify_test_one_event_two_eventgroups_client.cpp @@ -249,8 +249,10 @@ public: for (int i = 0; i < 3; i++) { // set value set_field_at_service(0x1); - std::unique_lock<std::mutex> its_set_value_lock(set_value_mutex_); - wait_on_condition(std::move(its_set_value_lock), &wait_set_value_, std::move(set_value_condition_), 30); + { + std::unique_lock<std::mutex> its_set_value_lock(set_value_mutex_); + wait_on_condition(std::move(its_set_value_lock), &wait_set_value_, std::move(set_value_condition_), 30); + } // subscribe std::unique_lock<std::mutex> its_events_lock(events_mutex_); @@ -269,7 +271,10 @@ public: // set value again set_field_at_service(0x2); - wait_on_condition(std::move(its_set_value_lock), &wait_set_value_, std::move(set_value_condition_), 30); + { + std::unique_lock<std::mutex> its_set_value_lock(set_value_mutex_); + wait_on_condition(std::move(its_set_value_lock), &wait_set_value_, std::move(set_value_condition_), 30); + } wait_for_events(std::move(its_events_lock), 3, std::move(events_condition_)); check_received_events_payload(0x2); @@ -283,7 +288,10 @@ public: // set value again set_field_at_service(0x3); - wait_on_condition(std::move(its_set_value_lock), &wait_set_value_, std::move(set_value_condition_), 30); + { + std::unique_lock<std::mutex> its_set_value_lock(set_value_mutex_); + wait_on_condition(std::move(its_set_value_lock), &wait_set_value_, std::move(set_value_condition_), 30); + } wait_for_events(std::move(its_events_lock), 3, std::move(events_condition_)); check_received_events_payload(0x3); its_expected.insert({info_.event_id, 1}); diff --git a/vsomeipConfig.cmake.in b/vsomeipConfig.cmake.in index e2c03cf..71e3ebb 100644 --- a/vsomeipConfig.cmake.in +++ b/vsomeipConfig.cmake.in @@ -4,7 +4,7 @@ # Compute paths
get_filename_component (VSOMEIP_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
-set (VSOMEIP_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@")
+get_filename_component(VSOMEIP_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@" REALPATH)
# Our library dependencies (contains definitions for IMPORTED targets)
if (NOT TARGET vsomeip AND NOT vsomeip_BINARY_DIR)
|