suricata
app-layer-enip-common.c
Go to the documentation of this file.
1 /* Copyright (C) 2015 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Kevin Wong <kwong@solananetworks.com>
22  *
23  * App-layer parser for ENIP protocol common code
24  *
25  */
26 
27 #include "suricata-common.h"
28 #include "util-unittest.h"
29 #include "util-unittest-helper.h"
30 #include "detect-parse.h"
31 #include "detect-engine.h"
32 #include "util-byte.h"
33 #include "pkt-var.h"
34 #include "util-profiling.h"
35 
36 #include "app-layer-enip-common.h"
37 
38 /**
39  * \brief Extract 8 bits and move up the offset
40  * @param res
41  * @param input
42  * @param offset
43  */
44 static int ENIPExtractUint8(uint8_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
45 {
46 
47  if (input_len < sizeof(uint8_t) || *offset > (input_len - sizeof(uint8_t)))
48  {
49  SCLogDebug("ENIPExtractUint8: Parsing beyond payload length");
50  return 0;
51  }
52 
53  *res = *(input + *offset);
54  *offset += sizeof(uint8_t);
55  return 1;
56 }
57 
58 /**
59  * \brief Extract 16 bits and move up the offset
60  * @param res
61  * @param input
62  * @param offset
63  */
64 static int ENIPExtractUint16(uint16_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
65 {
66 
67  if (input_len < sizeof(uint16_t) || *offset > (input_len - sizeof(uint16_t)))
68  {
69  SCLogDebug("ENIPExtractUint16: Parsing beyond payload length");
70  return 0;
71  }
72 
73  ByteExtractUint16(res, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
74  (const uint8_t *) (input + *offset));
75  *offset += sizeof(uint16_t);
76  return 1;
77 }
78 
79 /**
80  * \brief Extract 32 bits and move up the offset
81  * @param res
82  * @param input
83  * @param offset
84  */
85 static int ENIPExtractUint32(uint32_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
86 {
87 
88  if (input_len < sizeof(uint32_t) || *offset > (input_len - sizeof(uint32_t)))
89  {
90  SCLogDebug("ENIPExtractUint32: Parsing beyond payload length");
91  return 0;
92  }
93 
94  ByteExtractUint32(res, BYTE_LITTLE_ENDIAN, sizeof(uint32_t),
95  (const uint8_t *) (input + *offset));
96  *offset += sizeof(uint32_t);
97  return 1;
98 }
99 
100 /**
101  * \brief Extract 64 bits and move up the offset
102  * @param res
103  * @param input
104  * @param offset
105  */
106 static int ENIPExtractUint64(uint64_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
107 {
108 
109  if (input_len < sizeof(uint64_t) || *offset > (input_len - sizeof(uint64_t)))
110  {
111  SCLogDebug("ENIPExtractUint64: Parsing beyond payload length");
112  return 0;
113  }
114 
115  ByteExtractUint64(res, BYTE_LITTLE_ENDIAN, sizeof(uint64_t),
116  (const uint8_t *) (input + *offset));
117  *offset += sizeof(uint64_t);
118  return 1;
119 }
120 
121 
122 /**
123  * \brief Create service entry, add to transaction
124  * @param tx Transaction
125  * @return service entry
126  */
127 static CIPServiceEntry *CIPServiceAlloc(ENIPTransaction *tx)
128 {
129 
131  sizeof(CIPServiceEntry));
132  if (unlikely(svc == NULL))
133  return NULL;
134 
135  memset(svc, 0x00, sizeof(CIPServiceEntry));
136 
137  TAILQ_INIT(&svc->segment_list);
138  TAILQ_INIT(&svc->attrib_list);
139 
140  TAILQ_INSERT_TAIL(&tx->service_list, svc, next);
141  tx->service_count++;
142  return svc;
143 
144 }
145 
146 #if 0
147 /**
148  * \brief Delete service entry
149  */
150 
151 static void CIPServiceFree(void *s)
152 {
153  SCEnter();
154  if (s)
155  {
156  CIPServiceEntry *svc = (CIPServiceEntry *) s;
157 
158  SegmentEntry *seg = NULL;
159  while ((seg = TAILQ_FIRST(&svc->segment_list)))
160  {
161  TAILQ_REMOVE(&svc->segment_list, seg, next);
162  SCFree(seg);
163  }
164 
165  AttributeEntry *attr = NULL;
166  while ((attr = TAILQ_FIRST(&svc->attrib_list)))
167  {
168  TAILQ_REMOVE(&svc->attrib_list, attr, next);
169  SCFree(attr);
170  }
171 
172  SCFree(s);
173  }
174  SCReturn;
175 }
176 #endif
177 
178 /**
179  * \brief Decode ENIP Encapsulation Header
180  * @param input, input_len data stream
181  * @param enip_data stores data from Packet
182  * @return 1 Packet ok
183  * @return 0 Packet has errors
184  */
185 int DecodeENIPPDU(const uint8_t *input, uint32_t input_len,
186  ENIPTransaction *enip_data)
187 {
188  int ret = 1;
189 
190  uint16_t offset = 0; //byte offset
191 
192  //Decode Encapsulation Header
193  uint16_t cmd;
194  uint16_t len;
195  uint32_t session;
196  uint32_t status;
197  uint64_t context;
198  uint32_t option;
199  if (ENIPExtractUint16(&cmd, input, &offset, input_len) != 1)
200  {
201  return 0;
202  }
203  if (ENIPExtractUint16(&len, input, &offset, input_len) != 1)
204  {
205  return 0;
206  }
207  if (ENIPExtractUint32(&session, input, &offset, input_len) != 1)
208  {
209  return 0;
210  }
211  if (ENIPExtractUint32(&status, input, &offset, input_len) != 1)
212  {
213  return 0;
214  }
215  if (ENIPExtractUint64(&context, input, &offset, input_len) != 1)
216  {
217  return 0;
218  }
219  if (ENIPExtractUint32(&option, input, &offset, input_len) != 1)
220  {
221  return 0;
222  }
223 
224  enip_data->header.command = cmd;
225  enip_data->header.length = len;
226  enip_data->header.session = session;
227  enip_data->header.status = status;
228  enip_data->header.context = context;
229  enip_data->header.option = option;
230 
231  switch (enip_data->header.command)
232  {
233  case NOP:
234  SCLogDebug("DecodeENIP - NOP");
235  break;
236  case LIST_SERVICES:
237  SCLogDebug("DecodeENIP - LIST_SERVICES");
238  break;
239  case LIST_IDENTITY:
240  SCLogDebug("DecodeENIP - LIST_IDENTITY");
241  break;
242  case LIST_INTERFACES:
243  SCLogDebug("DecodeENIP - LIST_INTERFACES");
244  break;
245  case REGISTER_SESSION:
246  SCLogDebug("DecodeENIP - REGISTER_SESSION");
247  break;
248  case UNREGISTER_SESSION:
249  SCLogDebug("DecodeENIP - UNREGISTER_SESSION");
250  break;
251  case SEND_RR_DATA:
252  SCLogDebug(
253  "DecodeENIP - SEND_RR_DATA - parse Common Packet Format");
254  ret = DecodeCommonPacketFormatPDU(input, input_len, enip_data,
255  offset);
256  break;
257  case SEND_UNIT_DATA:
258  SCLogDebug(
259  "DecodeENIP - SEND UNIT DATA - parse Common Packet Format");
260  ret = DecodeCommonPacketFormatPDU(input, input_len, enip_data,
261  offset);
262  break;
263  case INDICATE_STATUS:
264  SCLogDebug("DecodeENIP - INDICATE_STATUS");
265  break;
266  case CANCEL:
267  SCLogDebug("DecodeENIP - CANCEL");
268  break;
269  default:
270  SCLogDebug("DecodeENIP - UNSUPPORTED COMMAND 0x%x",
271  enip_data->header.command);
272  }
273 
274  return ret;
275 }
276 
277 
278 /**
279  * \brief Decode Common Packet Format
280  * @param input, input_len data stream
281  * @param enip_data stores data from Packet
282  * @param offset current point in the packet
283  * @return 1 Packet ok
284  * @return 0 Packet has errors
285  */
286 int DecodeCommonPacketFormatPDU(const uint8_t *input, uint32_t input_len,
287  ENIPTransaction *enip_data, uint16_t offset)
288 {
289 
290  if (enip_data->header.length < sizeof(ENIPEncapDataHdr))
291  {
292  SCLogDebug("DecodeCommonPacketFormat: Malformed ENIP packet");
293  return 0;
294  }
295 
296  uint32_t handle;
297  uint16_t timeout;
298  uint16_t count;
299  if (ENIPExtractUint32(&handle, input, &offset, input_len) != 1)
300  {
301  return 0;
302  }
303  if (ENIPExtractUint16(&timeout, input, &offset, input_len) != 1)
304  {
305  return 0;
306  }
307  if (ENIPExtractUint16(&count, input, &offset, input_len) != 1)
308  {
309  return 0;
310  }
311  enip_data->encap_data_header.interface_handle = handle;
312  enip_data->encap_data_header.timeout = timeout;
313  enip_data->encap_data_header.item_count = count;
314 
315  uint16_t address_type;
316  uint16_t address_length; //length of connection id in bytes
317  uint32_t address_connectionid = 0;
318  uint32_t address_sequence = 0;
319 
320  if (ENIPExtractUint16(&address_type, input, &offset, input_len) != 1)
321  {
322  return 0;
323  }
324  if (ENIPExtractUint16(&address_length, input, &offset, input_len) != 1)
325  {
326  return 0;
327  }
328 
329  //depending on addr type, get connection id, sequence if needed. Can also use addr length too?
330  if (address_type == CONNECTION_BASED)
331  { //get 4 byte connection id
332  if (ENIPExtractUint32(&address_connectionid, input, &offset, input_len) != 1)
333  {
334  return 0;
335  }
336  } else if (address_type == SEQUENCE_ADDR_ITEM)
337  { // get 4 byte connection id and 4 byte sequence
338  if (ENIPExtractUint32(&address_connectionid, input, &offset, input_len) != 1)
339  {
340  return 0;
341  }
342  if (ENIPExtractUint32(&address_sequence, input, &offset, input_len) != 1)
343  {
344  return 0;
345  }
346  }
347 
348  enip_data->encap_addr_item.type = address_type;
349  enip_data->encap_addr_item.length = address_length;
350  enip_data->encap_addr_item.conn_id = address_connectionid;
351  enip_data->encap_addr_item.sequence_num = address_sequence;
352 
353  uint16_t data_type;
354  uint16_t data_length; //length of data in bytes
355  uint16_t data_sequence_count;
356 
357  if (ENIPExtractUint16(&data_type, input, &offset, input_len) != 1)
358  {
359  return 0;
360  }
361  if (ENIPExtractUint16(&data_length, input, &offset, input_len) != 1)
362  {
363  return 0;
364  }
365 
366  enip_data->encap_data_item.type = data_type;
367  enip_data->encap_data_item.length = data_length;
368 
369  if (enip_data->encap_data_item.type == CONNECTED_DATA_ITEM)
370  { //connected data items have seq number
371  if (ENIPExtractUint16(&data_sequence_count, input, &offset, input_len) != 1)
372  {
373  return 0;
374  }
375  enip_data->encap_data_item.sequence_count = data_sequence_count;
376  }
377 
378  switch (enip_data->encap_data_item.type)
379  {
380  case CONNECTED_DATA_ITEM:
381  SCLogDebug(
382  "DecodeCommonPacketFormat - CONNECTED DATA ITEM - parse CIP");
383  DecodeCIPPDU(input, input_len, enip_data, offset);
384  break;
386  SCLogDebug("DecodeCommonPacketFormat - UNCONNECTED DATA ITEM");
387  DecodeCIPPDU(input, input_len, enip_data, offset);
388  break;
389  default:
390  SCLogDebug("DecodeCommonPacketFormat - UNKNOWN TYPE 0x%x",
391  enip_data->encap_data_item.type);
392  return 0;
393  }
394 
395  return 1;
396 }
397 
398 /**
399  * \brief Decode CIP packet
400  * @param input, input_len data stream
401  * @param enip_data stores data from Packet
402  * @param offset current point in the packet
403  * @return 1 Packet ok
404  * @return 0 Packet has errors
405  */
406 
407 int DecodeCIPPDU(const uint8_t *input, uint32_t input_len,
408  ENIPTransaction *enip_data, uint16_t offset)
409 {
410  int ret = 1;
411 
412  if (enip_data->encap_data_item.length == 0)
413  {
414  SCLogDebug("DecodeCIP: No CIP Data");
415  return 0;
416  }
417 
418  if (offset > (input_len - sizeof(uint8_t)))
419  {
420  SCLogDebug("DecodeCIP: Parsing beyond payload length");
421  return 0;
422  }
423 
424  uint8_t service = 0;
425  service = *(input + offset);
426 
427  //SCLogDebug("CIP Service 0x%x", service);
428 
429  //use service code first bit to determine request/response, no need to save or push offset
430  if (service >> 7)
431  {
432  ret = DecodeCIPResponsePDU(input, input_len, enip_data, offset);
433  } else
434  {
435  ret = DecodeCIPRequestPDU(input, input_len, enip_data, offset);
436  }
437 
438  return ret;
439 }
440 
441 
442 
443 /**
444  * \brief Decode CIP Request
445  * @param input, input_len data stream
446  * @param enip_data stores data from Packet
447  * @param offset current point in the packet
448  * @return 1 Packet ok
449  * @return 0 Packet has errors
450  */
451 int DecodeCIPRequestPDU(const uint8_t *input, uint32_t input_len,
452  ENIPTransaction *enip_data, uint16_t offset)
453 {
454  int ret = 1;
455 
456  if (enip_data->encap_data_item.length < sizeof(CIPReqHdr))
457  {
458  SCLogDebug("DecodeCIPRequest - Malformed CIP Data");
459  return 0;
460  }
461 
462  uint8_t service = 0; //<-----CIP SERVICE
463  uint8_t path_size = 0;
464 
465  if (ENIPExtractUint8(&service, input, &offset, input_len) != 1)
466  {
467  return 0;
468  }
469  if (ENIPExtractUint8(&path_size, input, &offset, input_len) != 1)
470  {
471  return 0;
472  }
473 
474  if (service > MAX_CIP_SERVICE)
475  { // service codes of value 0x80 or greater are not permitted because in the CIP protocol the highest order bit is used to flag request(0)/response(1)
476  SCLogDebug("DecodeCIPRequest - INVALID CIP SERVICE 0x%x", service);
477  return 0;
478  }
479 
480  //reached maximum number of services
481  if (enip_data->service_count > 32)
482  {
483  SCLogDebug("DecodeCIPRequest: Maximum services reached");
484  return 0;
485  }
486 
487  //save CIP data
488  CIPServiceEntry *node = CIPServiceAlloc(enip_data);
489  if (node == NULL)
490  {
491  SCLogDebug("DecodeCIPRequest: Unable to create CIP service");
492  return 0;
493  }
494  node->direction = 0;
495  node->service = service;
496  node->request.path_size = path_size;
497  node->request.path_offset = offset;
498  // SCLogDebug("DecodeCIPRequestPDU: service 0x%x size %d", node->service,
499  // node->request.path_size);
500 
501  DecodeCIPRequestPathPDU(input, input_len, node, offset);
502 
503  offset += path_size * sizeof(uint16_t); //move offset past pathsize
504 
505  //list of CIP services is large and can be vendor specific, store CIP service anyways and let the rule decide the action
506  switch (service)
507  {
508  case CIP_RESERVED:
509  SCLogDebug("DecodeCIPRequest - CIP_RESERVED");
510  break;
511  case CIP_GET_ATTR_ALL:
512  SCLogDebug("DecodeCIPRequest - CIP_GET_ATTR_ALL");
513  break;
514  case CIP_GET_ATTR_LIST:
515  SCLogDebug("DecodeCIPRequest - CIP_GET_ATTR_LIST");
516  break;
517  case CIP_SET_ATTR_LIST:
518  SCLogDebug("DecodeCIPRequest - CIP_SET_ATTR_LIST");
519  break;
520  case CIP_RESET:
521  SCLogDebug("DecodeCIPRequest - CIP_RESET");
522  break;
523  case CIP_START:
524  SCLogDebug("DecodeCIPRequest - CIP_START");
525  break;
526  case CIP_STOP:
527  SCLogDebug("DecodeCIPRequest - CIP_STOP");
528  break;
529  case CIP_CREATE:
530  SCLogDebug("DecodeCIPRequest - CIP_CREATE");
531  break;
532  case CIP_DELETE:
533  SCLogDebug("DecodeCIPRequest - CIP_DELETE");
534  break;
535  case CIP_MSP:
536  SCLogDebug("DecodeCIPRequest - CIP_MSP");
537  DecodeCIPRequestMSPPDU(input, input_len, enip_data, offset);
538  break;
539  case CIP_APPLY_ATTR:
540  SCLogDebug("DecodeCIPRequest - CIP_APPLY_ATTR");
541  break;
542  case CIP_KICK_TIMER:
543  SCLogDebug("DecodeCIPRequest - CIP_KICK_TIMER");
544  break;
545  case CIP_OPEN_CONNECTION:
546  SCLogDebug("DecodeCIPRequest - CIP_OPEN_CONNECTION");
547  break;
548  case CIP_CHANGE_START:
549  SCLogDebug("DecodeCIPRequest - CIP_CHANGE_START");
550  break;
551  case CIP_GET_STATUS:
552  SCLogDebug("DecodeCIPRequest - CIP_GET_STATUS");
553  break;
554  default:
555  SCLogDebug("DecodeCIPRequest - CIP SERVICE 0x%x", service);
556  }
557 
558  return ret;
559 }
560 
561 
562 /**
563  * \brief Deocde CIP Request Path
564  * @param input, input_len data stream
565  * @param enip_data stores data from Packet
566  * @param offset current point in the packet
567  * @param cipserviced the cip service rule
568  * @return 1 Packet matches
569  * @return 0 Packet not match
570  */
571 int DecodeCIPRequestPathPDU(const uint8_t *input, uint32_t input_len,
572  CIPServiceEntry *node, uint16_t offset)
573 {
574  //SCLogDebug("DecodeCIPRequestPath: service 0x%x size %d length %d",
575  // node->service, node->request.path_size, input_len);
576 
577  if (node->request.path_size < 1)
578  {
579  //SCLogDebug("DecodeCIPRequestPath: empty path or CIP Response");
580  return 0;
581  }
582 
583  int bytes_remain = node->request.path_size;
584 
585  uint8_t reserved; //unused byte reserved by ODVA
586 
587  //8 bit fields
588  uint8_t req_path_instance8;
589  uint8_t req_path_attr8;
590 
591  //16 bit fields
592  uint16_t req_path_class16;
593  uint16_t req_path_instance16;
594 
595  uint16_t class = 0;
596 
597  SegmentEntry *seg = NULL;
598 
599  while (bytes_remain > 0)
600  {
601  uint8_t segment = 0;
602  if (ENIPExtractUint8(&segment, input, &offset, input_len) != 1)
603  {
604  return 0;
605  }
606  switch (segment)
607  { //assume order is class then instance. Can have multiple
608  case PATH_CLASS_8BIT: {
609  uint8_t req_path_class8 = 0;
610  if (ENIPExtractUint8(&req_path_class8, input, &offset, input_len) != 1) {
611  return 0;
612  }
613  class = (uint16_t) req_path_class8;
614  SCLogDebug("DecodeCIPRequestPathPDU: 8bit class 0x%x", class);
615 
616  seg = SCMalloc(sizeof(SegmentEntry));
617  if (unlikely(seg == NULL))
618  return 0;
619  seg->segment = segment;
620  seg->value = class;
621  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
622 
623  bytes_remain--;
624  break;
625  }
626  case PATH_INSTANCE_8BIT:
627  if (ENIPExtractUint8(&req_path_instance8, input, &offset, input_len) != 1)
628  {
629  return 0;
630  }
631  //skip instance, don't need to store
632  bytes_remain--;
633  break;
634  case PATH_ATTR_8BIT: //single attribute
635  if (ENIPExtractUint8(&req_path_attr8, input, &offset, input_len) != 1)
636  {
637  return 0;
638  }
639  //uint16_t attrib = (uint16_t) req_path_attr8;
640  //SCLogDebug("DecodeCIPRequestPath: 8bit attr 0x%x", attrib);
641 
642  seg = SCMalloc(sizeof(SegmentEntry));
643  if (unlikely(seg == NULL))
644  return 0;
645  seg->segment = segment;
646  seg->value = class;
647  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
648 
649  bytes_remain--;
650  break;
651  case PATH_CLASS_16BIT:
652  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1) //skip reserved
653  {
654  return 0;
655  }
656  if (ENIPExtractUint16(&req_path_class16, input, &offset, input_len) != 1)
657  {
658  return 0;
659  }
660  class = req_path_class16;
661  SCLogDebug("DecodeCIPRequestPath: 16bit class 0x%x", class);
662 
663  seg = SCMalloc(sizeof(SegmentEntry));
664  if (unlikely(seg == NULL))
665  return 0;
666  seg->segment = segment;
667  seg->value = class;
668  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
669  if (bytes_remain >= 2)
670  {
671  bytes_remain = bytes_remain - 2;
672  } else
673  {
674  bytes_remain = 0;
675  }
676  break;
677  case PATH_INSTANCE_16BIT:
678  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1) // skip reserved
679  {
680  return 0;
681  }
682  if (ENIPExtractUint16(&req_path_instance16, input, &offset, input_len) != 1)
683  {
684  return 0;
685  }
686  //skip instance, don't need to store
687  if (bytes_remain >= 2)
688  {
689  bytes_remain = bytes_remain - 2;
690  } else
691  {
692  bytes_remain = 0;
693  }
694  break;
695  default:
696  SCLogDebug(
697  "DecodeCIPRequestPath: UNKNOWN SEGMENT 0x%x service 0x%x",
698  segment, node->service);
699  return 0;
700  }
701  }
702 
703  if ((node->service == CIP_SET_ATTR_LIST) || (node->service
704  == CIP_GET_ATTR_LIST))
705  {
706  uint16_t attr_list_count;
707  uint16_t attribute;
708  //parse get/set attribute list
709 
710  if (ENIPExtractUint16(&attr_list_count, input, &offset, input_len) != 1)
711  {
712  return 0;
713  }
714  SCLogDebug("DecodeCIPRequestPathPDU: attribute list count %d",
715  attr_list_count);
716  for (int i = 0; i < attr_list_count; i++)
717  {
718  if (ENIPExtractUint16(&attribute, input, &offset, input_len) != 1)
719  {
720  return 0;
721  }
722  SCLogDebug("DecodeCIPRequestPathPDU: attribute %d", attribute);
723  //save attrs
724  AttributeEntry *attr = SCMalloc(sizeof(AttributeEntry));
725  if (unlikely(attr == NULL))
726  return 0;
727  attr->attribute = attribute;
728  TAILQ_INSERT_TAIL(&node->attrib_list, attr, next);
729 
730  }
731  }
732 
733  return 1;
734 }
735 
736 /**
737  * \brief Decode CIP Response
738  * @param input, input_len data stream
739  * @param enip_data stores data from Packet
740  * @param offset current point in the packet
741  * @return 1 Packet ok
742  * @return 0 Packet has errors
743  */
744 int DecodeCIPResponsePDU(const uint8_t *input, uint32_t input_len,
745  ENIPTransaction *enip_data, uint16_t offset)
746 {
747  int ret = 1;
748 
749  if (enip_data->encap_data_item.length < sizeof(CIPRespHdr))
750  {
751  SCLogDebug("DecodeCIPResponse - Malformed CIP Data");
752  return 0;
753  }
754 
755  uint8_t service = 0; //<----CIP SERVICE
756  uint8_t reserved; //unused byte reserved by ODVA
757  uint16_t status;
758 
759  if (ENIPExtractUint8(&service, input, &offset, input_len) != 1)
760  {
761  return 0;
762  }
763  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1)
764  {
765  return 0;
766  }
767  if (ENIPExtractUint16(&status, input, &offset, input_len) != 1)
768  {
769  return 0;
770  }
771 
772  //SCLogDebug("DecodeCIPResponse: service 0x%x",service);
773  service &= 0x7f; //strip off top bit to get service code. Responses have first bit as 1
774 
775  SCLogDebug("CIP service 0x%x status 0x%x", service, status);
776 
777  //reached maximum number of services
778  if (enip_data->service_count > 32)
779  {
780  SCLogDebug("DecodeCIPRequest: Maximum services reached");
781  return 0;
782  }
783 
784  //save CIP data
785  CIPServiceEntry *node = CIPServiceAlloc(enip_data);
786  if (node == NULL)
787  {
788  SCLogDebug("DecodeCIPRequest: Unable to create CIP service");
789  return 0;
790  }
791  node->direction = 1;
792  node->service = service;
793  node->response.status = status;
794 
795  SCLogDebug("DecodeCIPResponsePDU: service 0x%x size %d", node->service,
796  node->request.path_size);
797 
798  //list of CIP services is large and can be vendor specific, store CIP service anyways and let the rule decide the action
799  switch (service)
800  {
801  case CIP_RESERVED:
802  SCLogDebug("DecodeCIPResponse - CIP_RESERVED");
803  break;
804  case CIP_GET_ATTR_ALL:
805  SCLogDebug("DecodeCIPResponse - CIP_GET_ATTR_ALL");
806  break;
807  case CIP_GET_ATTR_LIST:
808  SCLogDebug("DecodeCIPResponse - CIP_GET_ATTR_LIST");
809  break;
810  case CIP_SET_ATTR_LIST:
811  SCLogDebug("DecodeCIPResponse - CIP_SET_ATTR_LIST");
812  break;
813  case CIP_RESET:
814  SCLogDebug("DecodeCIPResponse - CIP_RESET");
815  break;
816  case CIP_START:
817  SCLogDebug("DecodeCIPResponse - CIP_START");
818  break;
819  case CIP_STOP:
820  SCLogDebug("DecodeCIPResponse - CIP_STOP");
821  break;
822  case CIP_CREATE:
823  SCLogDebug("DecodeCIPResponse - CIP_CREATE");
824  break;
825  case CIP_DELETE:
826  SCLogDebug("DecodeCIPResponse - CIP_DELETE");
827  break;
828  case CIP_MSP:
829  SCLogDebug("DecodeCIPResponse - CIP_MSP");
830  DecodeCIPResponseMSPPDU(input, input_len, enip_data, offset);
831  break;
832  case CIP_APPLY_ATTR:
833  SCLogDebug("DecodeCIPResponse - CIP_APPLY_ATTR");
834  break;
835  case CIP_KICK_TIMER:
836  SCLogDebug("DecodeCIPResponse - CIP_KICK_TIMER");
837  break;
838  case CIP_OPEN_CONNECTION:
839  SCLogDebug("DecodeCIPResponse - CIP_OPEN_CONNECTION");
840  break;
841  case CIP_CHANGE_START:
842  SCLogDebug("DecodeCIPResponse - CIP_CHANGE_START");
843  break;
844  case CIP_GET_STATUS:
845  SCLogDebug("DecodeCIPResponse - CIP_GET_STATUS");
846  break;
847  default:
848  SCLogDebug("DecodeCIPResponse - CIP SERVICE 0x%x", service);
849  }
850 
851  return ret;
852 }
853 
854 
855 /**
856  * \brief Decode CIP Request Multi Service Packet
857  * @param input, input_len data stream
858  * @param enip_data stores data from Packet
859  * @param offset current point in the packet
860  * @return 1 Packet ok
861  * @return 0 Packet has errors
862  */
863 int DecodeCIPRequestMSPPDU(const uint8_t *input, uint32_t input_len,
864  ENIPTransaction *enip_data, uint16_t offset)
865 {
866  int ret = 1;
867  if (offset >= (input_len - sizeof(uint16_t)))
868  {
869  SCLogDebug("DecodeCIPRequestMSPPDU: Parsing beyond payload length");
870  return 0;
871  }
872  //use temp_offset just to grab the service offset, don't want to use and push offset
873  uint16_t temp_offset = offset;
874  uint16_t num_services;
875  ByteExtractUint16(&num_services, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
876  (const uint8_t *) (input + temp_offset));
877  temp_offset += sizeof(uint16_t);
878  //SCLogDebug("DecodeCIPRequestMSP number of services %d",num_services);
879 
880  for (int svc = 1; svc < num_services + 1; svc++)
881  {
882  if (temp_offset >= (input_len - sizeof(uint16_t)))
883  {
884  SCLogDebug("DecodeCIPRequestMSPPDU: Parsing beyond payload length");
885  return 0;
886  }
887 
888  uint16_t svc_offset; //read set of service offsets
889  ByteExtractUint16(&svc_offset, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
890  (const uint8_t *) (input + temp_offset));
891  temp_offset += sizeof(uint16_t);
892  //SCLogDebug("parseCIPRequestMSP service %d offset %d",svc, svc_offset);
893 
894  DecodeCIPPDU(input, input_len, enip_data, offset + svc_offset); //parse CIP at found offset
895  }
896 
897  return ret;
898 }
899 
900 
901 
902 /**
903  * \brief Decode CIP Response MultiService Packet.
904  * @param input, input_len data stream
905  * @param enip_data stores data from Packet
906  * @param offset current point in the packet
907  * @return 1 Packet ok
908  * @return 0 Packet has errors
909  */
910 int DecodeCIPResponseMSPPDU(const uint8_t *input, uint32_t input_len,
911  ENIPTransaction *enip_data, uint16_t offset)
912 {
913  int ret = 1;
914 
915  if (offset >= (input_len - sizeof(uint16_t)))
916  {
917  SCLogDebug("DecodeCIPResponseMSPPDU: Parsing beyond payload length");
918  return 0;
919  }
920  //use temp_offset just to grab the service offset, don't want to use and push offset
921  uint16_t temp_offset = offset;
922  uint16_t num_services;
923  ByteExtractUint16(&num_services, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
924  (const uint8_t *) (input + temp_offset));
925  temp_offset += sizeof(uint16_t);
926  //SCLogDebug("DecodeCIPResponseMSP number of services %d", num_services);
927 
928  for (int svc = 0; svc < num_services; svc++)
929  {
930  if (temp_offset >= (input_len - sizeof(uint16_t)))
931  {
932  SCLogDebug("DecodeCIPResponseMSP: Parsing beyond payload length");
933  return 0;
934  }
935 
936  uint16_t svc_offset; //read set of service offsets
937  ByteExtractUint16(&svc_offset, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
938  (const uint8_t *) (input + temp_offset));
939  temp_offset += sizeof(uint16_t);
940  //SCLogDebug("parseCIPResponseMSP service %d offset %d", svc, svc_offset);
941 
942  DecodeCIPPDU(input, input_len, enip_data, offset + svc_offset); //parse CIP at found offset
943  }
944 
945  return ret;
946 }
ENIPEncapDataItem encap_data_item
#define CIP_GET_STATUS
#define SCLogDebug(...)
Definition: util-debug.h:335
#define CIP_CREATE
#define TAILQ_FIRST(head)
Definition: queue.h:339
#define CIP_START
struct HtpBodyChunk_ * next
#define PATH_CLASS_8BIT
#define CIP_DELETE
#define CIP_RESET
#define PATH_INSTANCE_16BIT
#define unlikely(expr)
Definition: util-optimize.h:35
#define NOP
#define LIST_IDENTITY
#define CIP_GET_ATTR_LIST
uint64_t offset
int DecodeCIPRequestPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Request.
#define UNCONNECTED_DATA_ITEM
#define UNREGISTER_SESSION
int DecodeCIPResponseMSPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Response MultiService Packet.
#define MAX_CIP_SERVICE
#define SCCalloc(nm, a)
Definition: util-mem.h:253
int DecodeCIPResponsePDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Response.
#define PATH_ATTR_8BIT
ENIPEncapAddresItem encap_addr_item
#define TAILQ_INIT(head)
Definition: queue.h:370
int DecodeCIPRequestMSPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Request Multi Service Packet.
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:412
#define SEQUENCE_ADDR_ITEM
ENIPEncapDataHdr encap_data_header
#define SCEnter(...)
Definition: util-debug.h:337
#define CIP_GET_ATTR_ALL
#define REGISTER_SESSION
#define CONNECTED_DATA_ITEM
#define CIP_MSP
#define CIP_CHANGE_START
#define CIP_SET_ATTR_LIST
#define BYTE_LITTLE_ENDIAN
Definition: util-byte.h:30
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:385
#define CIP_APPLY_ATTR
#define CIP_STOP
#define SCMalloc(a)
Definition: util-mem.h:222
#define SEND_UNIT_DATA
#define CIP_KICK_TIMER
int DecodeENIPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data)
Decode ENIP Encapsulation Header.
int DecodeCommonPacketFormatPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode Common Packet Format.
#define SCFree(a)
Definition: util-mem.h:322
PoolThreadReserved res
#define INDICATE_STATUS
int DecodeCIPRequestPathPDU(const uint8_t *input, uint32_t input_len, CIPServiceEntry *node, uint16_t offset)
Deocde CIP Request Path.
#define PATH_CLASS_16BIT
#define CIP_OPEN_CONNECTION
int ByteExtractUint64(uint64_t *res, int e, uint16_t len, const uint8_t *bytes)
Definition: util-byte.c:122
int ByteExtractUint16(uint16_t *res, int e, uint16_t len, const uint8_t *bytes)
Definition: util-byte.c:164
struct CIPServiceEntry_::@5::@7 request
#define LIST_INTERFACES
#define SEND_RR_DATA
int DecodeCIPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP packet.
#define CIP_RESERVED
#define SCReturn
Definition: util-debug.h:339
#define CANCEL
uint8_t len
#define PATH_INSTANCE_8BIT
#define CONNECTION_BASED
#define LIST_SERVICES
struct CIPServiceEntry_::@5::@8 response
int ByteExtractUint32(uint32_t *res, int e, uint16_t len, const uint8_t *bytes)
Definition: util-byte.c:143