Line data Source code
1 : /********************************************************************************
2 : * Copyright (c) 2026 Accenture
3 : *
4 : * This program and the accompanying materials are made available under the
5 : * terms of the Apache License Version 2.0 which is available at
6 : * https://www.apache.org/licenses/LICENSE-2.0
7 : *
8 : * SPDX-License-Identifier: Apache-2.0
9 : ********************************************************************************/
10 :
11 : #include "someip/TcpServer.h"
12 :
13 : #include "someip/TcpConfig.h"
14 : #include "someip/logger.h"
15 :
16 : #include <etl/error_handler.h>
17 : #include <ip/to_str.h>
18 :
19 : // Logger API uses printf-style varargs for fixed diagnostic messages in this module.
20 : // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg)
21 :
22 : namespace someip
23 : {
24 : using ::ip::IPAddress;
25 : using ::ip::IPEndpoint;
26 : using ::tcp::AbstractServerSocket;
27 : using ::tcp::AbstractSocket;
28 : using ::util::logger::SOMEIP;
29 :
30 55 : TcpServer::TcpServer(::tcp::AbstractServerSocket& socket, TcpProxyConfig& tcpClientPool)
31 55 : : _socket(socket), _tcpClientPool(tcpClientPool), _pClientBufferPool(nullptr)
32 : {
33 55 : _socket.setSocketProvidingConnectionListener(*this);
34 55 : }
35 :
36 46 : bool TcpServer::isInitialized() const { return _tcpClientPool.getSize() > 0U; }
37 :
38 9 : AbstractServerSocket& TcpServer::getServerSocket() const { return _socket; }
39 :
40 1 : size_t TcpServer::getNumProxies() const { return _tcpClientPool.getSize(); }
41 :
42 2 : void TcpServer::setClientBufferPool(ClientBufferPool& buffers)
43 : {
44 2 : ETL_ASSERT(
45 : _tcpClientPool.getBufferType() == TcpProxyConfig::BufferType::External,
46 : ETL_ERROR_GENERIC("tcp client pool must use external buffer type"));
47 2 : _pClientBufferPool = &buffers;
48 2 : }
49 :
50 1 : TcpProxy* TcpServer::getProxy(size_t const pos) const { return _tcpClientPool.getProxy(pos); }
51 :
52 17 : bool TcpServer::isOpen() const { return isInitialized() && (!_socket.isClosed()); }
53 :
54 6 : bool TcpServer::open(IPEndpoint const& localEndpoint)
55 : {
56 6 : if ((_tcpClientPool.getBufferType() == TcpProxyConfig::BufferType::External)
57 6 : && (_pClientBufferPool == nullptr))
58 : {
59 0 : ERROR_LOG(SOMEIP, "TcpServer[%p]::open() no client reassembly buffer", this);
60 0 : return false;
61 : }
62 :
63 6 : if (!isInitialized())
64 : {
65 1 : ERROR_LOG(SOMEIP, "TcpServer[%p]::open() not initialized", this);
66 1 : return false;
67 : }
68 :
69 5 : if (isOpen())
70 : {
71 1 : return true;
72 : }
73 :
74 4 : if (!localEndpoint.isSet())
75 : {
76 0 : ERROR_LOG(SOMEIP, "TcpServer[%p]::open() no local endpoint", this);
77 0 : return false;
78 : }
79 :
80 : char localEndpointStr[::ip::MAX_ENDPOINT_STRING_LENGTH];
81 4 : INFO_LOG(
82 : SOMEIP,
83 : "TcpServer[%p]::open() at: %s",
84 : this,
85 : ::ip::to_str(localEndpoint, localEndpointStr).data());
86 :
87 4 : if (!_socket.bind(localEndpoint.getAddress(), localEndpoint.getPort()))
88 : {
89 1 : ERROR_LOG(SOMEIP, "TcpServer[%p]::open() bind failed", this);
90 1 : return false;
91 : }
92 :
93 3 : if (!_socket.accept())
94 : {
95 1 : ERROR_LOG(SOMEIP, "TcpServer[%p]::open() accept failed", this);
96 1 : return false;
97 : }
98 :
99 2 : return true;
100 : }
101 :
102 2 : void TcpServer::close()
103 : {
104 2 : if (!isOpen())
105 : {
106 1 : return;
107 : }
108 :
109 1 : INFO_LOG(SOMEIP, "TcpServer[%p]: close", this);
110 :
111 1 : _socket.close();
112 : }
113 :
114 11 : ::etl::expected<uint16_t, PortError> TcpServer::getLocalPort() const
115 : {
116 11 : if (isInitialized())
117 : {
118 11 : return _socket.getLocalPort();
119 : }
120 :
121 0 : return ::etl::unexpected<PortError>(PortError::NOT_INITIALIZED);
122 : }
123 :
124 : // virtual
125 9 : AbstractSocket* TcpServer::getSocket(IPAddress const& ipAddr, uint16_t const port)
126 : {
127 9 : if (!isInitialized())
128 : {
129 1 : ERROR_LOG(SOMEIP, "TcpServer[%p]::getSocket() not initialized", this);
130 1 : return nullptr;
131 : }
132 :
133 8 : if (::ip::isUnspecified(ipAddr))
134 : {
135 0 : ERROR_LOG(SOMEIP, "TcpServer[%p]::getSocket() no address", this);
136 0 : return nullptr;
137 : }
138 :
139 : // First search if this is the same client connecting again.
140 : // This would be a sign that we didn't catch the connection close correctly. In this case we
141 : // have to abort the existing connection and reuse it.
142 : // This also means that only one TCP client connection is allowed between
143 : // a remote and the local ECU, which holds true by current requirements.
144 :
145 8 : ::ip::IPEndpoint const endpoint(ipAddr, port);
146 8 : auto const localPortResult = getLocalPort();
147 8 : TcpProxy* proxy = nullptr;
148 :
149 8 : if (localPortResult.has_value())
150 : {
151 8 : proxy = _tcpClientPool.getOpenProxy(localPortResult.value(), endpoint);
152 : }
153 :
154 8 : if (proxy != nullptr)
155 : {
156 1 : proxy->abort();
157 1 : WARN_LOG(SOMEIP, "TcpServer[%p]: reusing proxy[%p]", this, proxy);
158 : }
159 : else
160 : {
161 7 : proxy = _tcpClientPool.nextProxy();
162 : }
163 :
164 8 : if (proxy != nullptr)
165 : {
166 6 : if (_tcpClientPool.getBufferType() == TcpProxyConfig::BufferType::External)
167 : {
168 5 : ETL_ASSERT(
169 : _pClientBufferPool != nullptr,
170 : ETL_ERROR_GENERIC("client buffer pool must not be null"));
171 5 : if (_pClientBufferPool->empty())
172 : {
173 1 : ERROR_LOG(SOMEIP, "TcpServer[%p]::getSocket() no free client socket buffer", this);
174 1 : return nullptr;
175 : }
176 :
177 4 : proxy->setInternalBuffer(_pClientBufferPool->back());
178 4 : _pClientBufferPool->pop_back();
179 : }
180 :
181 5 : IPEndpoint const remote(ipAddr, port);
182 : char endpointStr[::ip::MAX_ENDPOINT_STRING_LENGTH];
183 :
184 5 : INFO_LOG(
185 : SOMEIP,
186 : "TcpServer[%p]: acquire proxy[%p] for: %s",
187 : this,
188 : proxy,
189 : ::ip::to_str(remote, endpointStr).data());
190 5 : proxy->_pParentServerListener = this;
191 5 : return &(proxy->getSocket());
192 : }
193 :
194 2 : ERROR_LOG(SOMEIP, "TcpServer[%p]::getSocket() no free socket", this);
195 2 : return nullptr;
196 : }
197 :
198 : // virtual
199 3 : void TcpServer::connectionAccepted(::tcp::AbstractSocket& socket)
200 : {
201 3 : socket.disableNagleAlgorithm();
202 :
203 3 : IPEndpoint const remote(socket.getRemoteIPAddress(), socket.getRemotePort());
204 : char endpointStr[::ip::MAX_ENDPOINT_STRING_LENGTH];
205 :
206 3 : uint16_t const localPort = socket.getLocalPort();
207 :
208 3 : INFO_LOG(
209 : SOMEIP,
210 : "TcpServer[%p]: accepted connection at port: %d from : %s",
211 : this,
212 : localPort,
213 : ::ip::to_str(remote, endpointStr).data());
214 :
215 3 : TcpProxy* const proxy = _tcpClientPool.getProxy(socket);
216 3 : if (proxy == nullptr)
217 : {
218 1 : ERROR_LOG(SOMEIP, "TcpServer[%p]::connectionAccepted() invalid socket", this);
219 : }
220 : else
221 : {
222 2 : proxy->incRefCounter();
223 2 : proxy->openConnection(localPort);
224 : }
225 3 : }
226 :
227 3 : void TcpServer::connectionChanged(TcpProxy& proxy)
228 : {
229 3 : if (!proxy.isOpen())
230 : {
231 2 : proxy.decRefCounter();
232 2 : if (_tcpClientPool.getBufferType() == TcpProxyConfig::BufferType::External)
233 : {
234 1 : ETL_ASSERT(
235 : _pClientBufferPool != nullptr,
236 : ETL_ERROR_GENERIC("client buffer pool must not be null"));
237 1 : _pClientBufferPool->push_back(proxy.getInternalBuffer());
238 : }
239 2 : proxy._pParentServerListener = nullptr;
240 : }
241 3 : }
242 :
243 : } // namespace someip
244 :
245 : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
|