session.cpp
Go to the documentation of this file.
1 
26 #include <boost/bind.hpp>
27 #include <boost/shared_ptr.hpp>
28 #include <boost/make_shared.hpp>
29 #include <boost/random/mersenne_twister.hpp>
30 #include <boost/random/uniform_int_distribution.hpp>
31 #include <console_bridge/console.h>
32 
41 
42 using boost::shared_ptr;
43 using boost::make_shared;
44 using std::endl;
45 
46 namespace eip {
47 
48 using serialization::BufferReader;
49 using serialization::BufferWriter;
50 
51 Session::Session(shared_ptr<Socket> socket, shared_ptr<Socket> io_socket,
52  EIP_UINT vendor_id, EIP_UDINT serial_num)
53  : socket_(socket), io_socket_(io_socket), session_id_(0),
54  my_vendor_id_(vendor_id), my_serial_num_(serial_num)
55 {
56  // generate pseudo-random connection ID and connection SN starting points
57  boost::random::mt19937 gen;
58  gen.seed(std::time(0));
59  boost::random::uniform_int_distribution<> dist(0, 0xFFFF);
60  next_connection_id_ = gen();
61  next_connection_sn_ = dist(gen);
62  CONSOLE_BRIDGE_logInform("Generated starting connection ID %zu and SN %zu", next_connection_id_, next_connection_sn_);
63 }
64 
66 {
67  try
68  {
69  if (session_id_ != 0)
70  {
71  close();
72  }
73  }
74  catch (...)
75  {
76  // can't throw exceptions, but can't do anything either
77  }
78 }
79 
80 void Session::open(string hostname, string port, string io_port)
81 {
82  CONSOLE_BRIDGE_logInform("Resolving hostname and connecting socket");
83  socket_->open(hostname, port);
84  io_socket_->open(hostname, io_port);
85 
86  // create the registration message
87  CONSOLE_BRIDGE_logInform("Creating and sending the registration message");
88  shared_ptr<RegisterSessionData> reg_data = make_shared<RegisterSessionData>();
89  EncapPacket reg_msg(EIP_CMD_REGISTER_SESSION, 0, reg_data);
90 
91  // send the register session message and get response
92  EncapPacket response;
93  try
94  {
95  response = sendCommand(reg_msg);
96  }
97  catch (std::length_error ex)
98  {
99  socket_->close();
100  io_socket_->close();
101  CONSOLE_BRIDGE_logError("Could not parse response when registering session: %s", ex.what());
102  throw std::runtime_error("Invalid response received registering session");
103  }
104  catch (std::logic_error ex)
105  {
106  socket_->close();
107  io_socket_->close();
108  CONSOLE_BRIDGE_logError("Error in registration response: %s", ex.what());
109  throw std::runtime_error("Error in registration response");
110  }
111 
112  if (response.getHeader().length != reg_data->getLength())
113  {
114  CONSOLE_BRIDGE_logWarn("Registration message received with wrong size. Expected %zu bytes, received %u",
115  reg_data->getLength(), response.getHeader().length);
116  }
117 
118  bool response_valid = false;
119  try
120  {
121  response.getPayloadAs(*reg_data);
122  response_valid = true;
123  }
124  catch (std::length_error ex)
125  {
126  CONSOLE_BRIDGE_logWarn("Registration message too short, ignoring");
127  }
128  catch (std::logic_error ex)
129  {
130  CONSOLE_BRIDGE_logWarn("Could not parse registration response: %s", ex.what());
131  }
132 
133  if (response_valid && reg_data->protocol_version != EIP_PROTOCOL_VERSION)
134  {
135  CONSOLE_BRIDGE_logError("Error: Wrong Ethernet Industrial Protocol Version. Expected %u got %u",
136  EIP_PROTOCOL_VERSION, reg_data->protocol_version);
137  socket_->close();
138  io_socket_->close();
139  throw std::runtime_error("Received wrong Ethernet IP Protocol Version on registration");
140  }
141  if (response_valid && reg_data->options != 0)
142  {
143  CONSOLE_BRIDGE_logWarn("Registration message included non-zero options flags: %u", reg_data->options);
144  }
145 
146  session_id_ = response.getHeader().session_handle;
147  CONSOLE_BRIDGE_logInform("Successfully opened session ID %zu", session_id_);
148 }
149 
151 {
152  // TODO: should close all connections and the IO port
153  CONSOLE_BRIDGE_logInform("Closing session");
154 
155  // create the unregister session message
157  socket_->send(reg_msg);
158 
159  CONSOLE_BRIDGE_logInform("Session closed");
160 
161  socket_->close();
162  io_socket_->close();
163  session_id_ = 0;
164 }
165 
167 {
168  CONSOLE_BRIDGE_logDebug("Sending Command");
169  socket_->send(req);
170 
171  CONSOLE_BRIDGE_logDebug("Waiting for response");
172  size_t n = socket_->receive(buffer(recv_buffer_));
173  CONSOLE_BRIDGE_logDebug("Received response of %zu bytes", n);
174 
175  BufferReader reader(buffer(recv_buffer_, n));
176  EncapPacket result;
177  result.deserialize(reader);
178 
179  if (reader.getByteCount() != n)
180  {
181  CONSOLE_BRIDGE_logWarn("Packet received with %zu bytes, but only %zu bytes used", n, reader.getByteCount());
182  }
183 
184  check_packet(result, req.getHeader().command);
185  return result;
186 }
187 
189 {
190  // verify that all fields are correct
191  if (pkt.getHeader().command != exp_cmd)
192  {
193  CONSOLE_BRIDGE_logError("Reply received with wrong command. Expected %u received %u", exp_cmd,
194  pkt.getHeader().command);
195  throw std::logic_error("Reply received with wrong command");
196  }
197  if (session_id_ == 0 && pkt.getHeader().session_handle == 0)
198  {
199  CONSOLE_BRIDGE_logError("Zero session handle received on registration: %zu", pkt.getHeader().session_handle);
200  throw std::logic_error("Zero session handle received on registration");
201  }
202  if (session_id_ != 0 && pkt.getHeader().session_handle != session_id_)
203  {
204  CONSOLE_BRIDGE_logError("Reply received with wrong session ID. Expected %zu, received %zu", session_id_,
205  pkt.getHeader().session_handle);
206  throw std::logic_error("Wrong session ID received for command");
207  }
208  if (pkt.getHeader().status != 0)
209  {
210  CONSOLE_BRIDGE_logWarn("Non-zero status received: %zu", pkt.getHeader().status);
211  }
212  if (pkt.getHeader().context[0] != 0 || pkt.getHeader().context[1] != 0)
213  {
214  CONSOLE_BRIDGE_logWarn("Non-zero sender context received: %zu/%zu", pkt.getHeader().context[0],
215  pkt.getHeader().context[1]);
216  }
217  if (pkt.getHeader().options != 0)
218  {
219  CONSOLE_BRIDGE_logWarn("Non-zero options received: %zu", pkt.getHeader().options);
220  }
221 }
222 
224  EIP_USINT attribute_id, Serializable& result)
225 {
226  shared_ptr<Serializable> no_data;
227  RRDataResponse resp_data = sendRRDataCommand(0x0E,
228  Path(class_id, instance_id, attribute_id), no_data);
229 
230  resp_data.getResponseDataAs(result);
231 }
232 
234  EIP_USINT instance_id, EIP_USINT attribute_id, shared_ptr<Serializable> data)
235 {
236  RRDataResponse resp_data = sendRRDataCommand(0x10,
237  Path(class_id, instance_id, attribute_id), data);
238 }
239 
241  shared_ptr<Serializable> data)
242 {
243  CONSOLE_BRIDGE_logDebug("Creating RR Data Request");
244  shared_ptr<RRDataRequest> req_data =
245  make_shared<RRDataRequest> (service, path, data);
246  EncapPacket encap_pkt(EIP_CMD_SEND_RR_DATA, session_id_, req_data);
247 
248  // send command and get response
249  EncapPacket response;
250  try
251  {
252  response = sendCommand(encap_pkt);
253  }
254  catch (std::length_error ex)
255  {
256  CONSOLE_BRIDGE_logError("Response packet to RR command too short: %s", ex.what());
257  throw std::runtime_error("Packet response to RR Data Command too short");
258  }
259  catch (std::logic_error ex)
260  {
261  CONSOLE_BRIDGE_logError("Invalid response to RR command: %s", ex.what());
262  throw std::runtime_error("Invalid packet response to RR Data Command");
263  }
264 
265  RRDataResponse resp_data;
266  try
267  {
268  response.getPayloadAs(resp_data);
269  }
270  catch (std::length_error ex)
271  {
272  CONSOLE_BRIDGE_logError("Response data to RR command too short: %s", ex.what());
273  throw std::runtime_error("Response data to RR Command too short");
274  }
275  catch (std::logic_error ex)
276  {
277  CONSOLE_BRIDGE_logError("Invalid data to RR command: %s", ex.what());
278  throw std::runtime_error("Invalid data in response to RR command");
279  }
280 
281  // check that responses are valid
282  if (resp_data.getServiceCode() != (service | 0x80))
283  {
284  CONSOLE_BRIDGE_logWarn("Wrong service code returned for RR Data command. Expected: %d but received %d",
285  (int)service, (int)resp_data.getServiceCode());
286  // throw std::runtime_error("Wrong service code returned for RR Data command");
287  }
288  if (resp_data.getGeneralStatus())
289  {
290  CONSOLE_BRIDGE_logError("RR Data Command failed with status %d", (int)resp_data.getGeneralStatus());
291  throw std::runtime_error("RR Data Command Failed");
292  }
293  return resp_data;
294 }
295 
297  const EIP_CONNECTION_INFO_T& t_to_o)
298 {
299  Connection conn(o_to_t, t_to_o);
305 
306  shared_ptr<ForwardOpenRequest> req = conn.createForwardOpenRequest();
307  RRDataResponse resp_data = sendRRDataCommand(0x5B, Path(0x06, 1), req);
308  ForwardOpenSuccess result;
309  resp_data.getResponseDataAs(result);
310  if (!conn.verifyForwardOpenResult(result))
311  {
312  CONSOLE_BRIDGE_logError("Received invalid response to forward open request");
313  throw std::logic_error("Forward Open Response Invalid");
314  }
315 
316  connections_.push_back(conn);
317  return connections_.size() - 1;
318 }
319 
321 {
322  shared_ptr<ForwardCloseRequest> req = connections_[n].createForwardCloseRequest();
323  RRDataResponse resp_data = sendRRDataCommand(0x4E, Path(0x06, 1), req);
324  ForwardCloseSuccess result;
325  resp_data.getResponseDataAs(result);
326  if (!connections_[n].verifyForwardCloseResult(result))
327  {
328  CONSOLE_BRIDGE_logError("Received invalid response to forward close request");
329  throw std::logic_error("Forward Close Response Invalid");
330  }
331  // remove the connection from the list
332  connections_.erase(connections_.begin() + n);
333 }
334 
336 {
337  CONSOLE_BRIDGE_logDebug("Receiving IO packet");
338  size_t n = io_socket_->receive(buffer(recv_buffer_));
339  CONSOLE_BRIDGE_logDebug("Received IO of %zu bytes", n);
340 
341  BufferReader reader(buffer(recv_buffer_, n));
342  CPFPacket result;
343  result.deserialize(reader);
344 
345  if (reader.getByteCount() != n)
346  {
347  CONSOLE_BRIDGE_logWarn("IO packet received with %zu bytes, but only %zu bytes used", n, reader.getByteCount());
348  }
349 
350  return result;
351 }
352 
354 {
355  CONSOLE_BRIDGE_logDebug("Sending CPF Packet on IO Socket");
356  io_socket_->send(pkt);
357 }
358 
359 
360 } // namespace eip
eip::Session::next_connection_id_
EIP_UDINT next_connection_id_
Definition: session.h:217
eip::Session::my_serial_num_
EIP_UDINT my_serial_num_
Definition: session.h:215
eip::CPFPacket
Definition: cpf_packet.h:49
eip_types.h
EIP_CONNECTION_INFO_T
Definition: eip_types.h:97
eip::ForwardCloseSuccess
Definition: forward_close_success.h:53
eip::Session::~Session
virtual ~Session()
Definition: session.cpp:65
eip::EncapHeader::options
EIP_DWORD options
Definition: encap_header.h:52
eip
Definition: connection.h:41
eip::RRDataResponse::getResponseDataAs
void getResponseDataAs(Serializable &result)
Definition: rr_data_response.h:113
eip::RRDataResponse::getGeneralStatus
EIP_USINT getGeneralStatus() const
Definition: rr_data_response.h:73
eip::Connection::connection_sn
EIP_UINT connection_sn
Definition: connection.h:52
eip::Connection::t_to_o_connection_id
EIP_UDINT t_to_o_connection_id
Definition: connection.h:55
eip::EncapHeader::context
EIP_DWORD context[2]
Definition: encap_header.h:51
eip::Session::Session
Session(shared_ptr< Socket > socket, shared_ptr< Socket > io_socket, EIP_UINT vendor_id=DEFAULT_VENDOR_ID, EIP_UDINT serial_num=DEFAULT_SERIAL_NUM)
Definition: session.cpp:51
eip::EncapHeader::status
EIP_DWORD status
Definition: encap_header.h:50
EIP_UINT
uint16_t EIP_UINT
Definition: eip_types.h:39
eip::Session::connections_
vector< Connection > connections_
Definition: session.h:219
rr_data_request.h
eip::EncapPacket::getHeader
EncapHeader & getHeader()
Definition: encap_packet.h:78
eip::Connection::verifyForwardOpenResult
bool verifyForwardOpenResult(const ForwardOpenSuccess &result)
Definition: connection.cpp:103
eip::Session::check_packet
void check_packet(EncapPacket &pkt, EIP_UINT exp_cmd)
Definition: session.cpp:188
eip::Session::sendCommand
EncapPacket sendCommand(EncapPacket &req)
Definition: session.cpp:166
eip::Session::io_socket_
shared_ptr< Socket > io_socket_
Definition: session.h:210
eip::ForwardOpenSuccess
Definition: forward_open_success.h:53
EIP_USINT
uint8_t EIP_USINT
Definition: eip_types.h:36
eip::Session::socket_
shared_ptr< Socket > socket_
Definition: session.h:209
eip::EncapHeader::session_handle
EIP_UDINT session_handle
Definition: encap_header.h:49
buffer_writer.h
eip::serialization::Serializable
Definition: serializable.h:38
eip::Connection::createForwardOpenRequest
shared_ptr< ForwardOpenRequest > createForwardOpenRequest()
Definition: connection.cpp:59
eip::Connection::originator_sn
EIP_UDINT originator_sn
Definition: connection.h:51
eip::Session::recv_buffer_
EIP_BYTE recv_buffer_[4 *1024]
Definition: session.h:212
eip::Connection::o_to_t_connection_id
EIP_UDINT o_to_t_connection_id
Definition: connection.h:54
EIP_PROTOCOL_VERSION
#define EIP_PROTOCOL_VERSION
Definition: eip_types.h:31
eip::Session::sendRRDataCommand
RRDataResponse sendRRDataCommand(EIP_USINT service, const Path &path, shared_ptr< Serializable > data)
Definition: session.cpp:240
eip::EncapPacket::getPayloadAs
void getPayloadAs(Serializable &result)
Definition: encap_packet.cpp:44
EIP_CMD_REGISTER_SESSION
@ EIP_CMD_REGISTER_SESSION
Definition: eip_types.h:65
eip::EncapHeader::command
EIP_UINT command
Definition: encap_header.h:47
eip::Session::createConnection
int createConnection(const EIP_CONNECTION_INFO_T &o_to_t, const EIP_CONNECTION_INFO_T &t_to_o)
Definition: session.cpp:296
EIP_CMD_UNREGISTER_SESSION
@ EIP_CMD_UNREGISTER_SESSION
Definition: eip_types.h:66
eip::serialization::BufferReader::getByteCount
virtual size_t getByteCount()
Definition: buffer_reader.h:118
eip::RRDataResponse
Definition: rr_data_response.h:56
eip::Session::sendIOPacket
void sendIOPacket(CPFPacket &pkt)
Definition: session.cpp:353
eip::Session::next_connection_sn_
EIP_UINT next_connection_sn_
Definition: session.h:216
eip::Session::setSingleAttributeSerializable
void setSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT instance_id, EIP_USINT attribute_id, shared_ptr< Serializable > data)
Definition: session.cpp:233
eip::Session::closeConnection
void closeConnection(size_t n)
Definition: session.cpp:320
eip::Session::open
void open(string hostname, string port="44818", string io_port="2222")
Definition: session.cpp:80
rr_data_response.h
eip::Connection
Definition: connection.h:46
EIP_CMD_SEND_RR_DATA
@ EIP_CMD_SEND_RR_DATA
Definition: eip_types.h:67
eip::Session::session_id_
EIP_UDINT session_id_
Definition: session.h:211
buffer_reader.h
encap_packet.h
session.h
eip::Session::receiveIOPacket
CPFPacket receiveIOPacket()
Definition: session.cpp:335
eip::serialization::BufferReader
Definition: buffer_reader.h:45
eip::Session::my_vendor_id_
EIP_UINT my_vendor_id_
Definition: session.h:214
eip::EncapPacket::deserialize
Reader & deserialize(Reader &reader, size_t length)
Definition: encap_packet.cpp:77
eip::Connection::originator_vendor_id
EIP_UINT originator_vendor_id
Definition: connection.h:50
eip::Session::close
void close()
Definition: session.cpp:150
eip::CPFPacket::deserialize
virtual Reader & deserialize(Reader &reader, size_t length)
Definition: cpf_packet.h:89
eip::EncapHeader::length
EIP_UINT length
Definition: encap_header.h:48
eip::RRDataResponse::getServiceCode
EIP_USINT getServiceCode() const
Definition: rr_data_response.h:64
register_session_data.h
eip::Session::getSingleAttributeSerializable
void getSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT instance_id, EIP_USINT attribute_id, Serializable &result)
Definition: session.cpp:223
eip::EncapPacket
Definition: encap_packet.h:50
EIP_UDINT
uint32_t EIP_UDINT
Definition: eip_types.h:42
eip::Path
Definition: path.h:50


odva_ethernetip
Author(s): Kareem Shehata
autogenerated on Wed Mar 2 2022 00:38:56