1 /* packet-ouch.c
2 * Routines for OUCH 4.x protocol dissection
3 * Copyright (C) 2013, 2015, 2016 David Arnold <d@0x1.org>
4 *
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 /* OUCH is a stock exchange order entry protocol published and used by
13 * NASDAQ. This dissector supports versions 4.x, which differ from
14 * earlier versions by adopting binary encoding for numeric values.
15 *
16 * OUCH is usually encapsulated within NASDAQ's SoupBinTCP protocol,
17 * running over a TCP connection from the trading application to the
18 * exchange. SOUP provides framing, heartbeats and authentication;
19 * consequently none of these is present in OUCH.
20 *
21 * Other exchanges have created order entry protocols very similar to
22 * OUCH, but typically they differ in subtle ways (and continue to
23 * diverge as time progresses) so I have not attempted to dissect
24 * anything other than proper NASDAQ OUCH in this code.
25 *
26 * Specifications are available from NASDAQ's website, although the
27 * links to find them tend to move around over time. At the time of
28 * writing, the correct URL is:
29 *
30 * http://www.nasdaqtrader.com/content/technicalsupport/specifications/TradingProducts/OUCH4.2.pdf
31 */
32
33
34 #include "config.h"
35
36 #include <epan/packet.h>
37
38 void proto_register_ouch(void);
39 void proto_reg_handoff_ouch(void);
40
41 static const value_string pkt_type_val[] = {
42 { 'O', "Enter Order" },
43 { 'U', "Replace Order" },
44 { 'X', "Cancel Order" },
45 { 'M', "Modify Order" },
46 { 'S', "System Event" },
47 { 'A', "Accepted" },
48 { 'R', "Replaced" }, /* 'U' on the wire, but use 'R' to disambiguate */
49 { 'C', "Canceled" },
50 { 'D', "AIQ Canceled" },
51 { 'E', "Executed" },
52 { 'F', "Trade Correction" },
53 { 'G', "Executed with Reference Price" },
54 { 'B', "Broken Trade" },
55 { 'K', "Price Correction" },
56 { 'J', "Rejected" },
57 { 'P', "Cancel Pending" },
58 { 'I', "Cancel Reject" },
59 { 'T', "Order Priority Update" },
60 { 'm', "Order Modified" }, /* 'M' on the wire; 'm' to disambiguate */
61 { 0, NULL }
62 };
63
64
65 static const value_string ouch_bbo_weight_indicator_val[] = {
66 { '0', "0 - 0.2%" },
67 { '1', "0.2 - 1%" },
68 { '2', "1 - 2%" },
69 { '3', "Greater than 2%" },
70 { ' ', "Unspecified" },
71 { 'S', "Sets the QBBO while joining the NBBO" },
72 { 'N', "Improves the NBBO upon entry" },
73 { 0, NULL }
74 };
75
76 static const value_string ouch_broken_trade_reason_val[] = {
77 { 'E', "Erroneous" },
78 { 'C', "Consent" },
79 { 'S', "Supervisory" },
80 { 'X', "External" },
81 { 0, NULL }
82 };
83
84 static const value_string ouch_buy_sell_indicator_val[] = {
85 { 'B', "Buy Order" },
86 { 'S', "Sell Order" },
87 { 'T', "Sell Short" },
88 { 'E', "Sell Short Exempt" },
89 { 0, NULL }
90 };
91
92 static const value_string ouch_cancel_reason_val[] = {
93 { 'C', "Cross cancel" },
94 { 'D', "Regulatory restriction" },
95 { 'E', "Closed" },
96 { 'H', "Halted" },
97 { 'I', "Immediate or Cancel order" },
98 { 'K', "Market Collars" },
99 { 'Q', "Self-match prevention" },
100 { 'S', "Supervisory" },
101 { 'T', "Timeout" },
102 { 'U', "User requested cancel" },
103 { 'X', "Open Protection" },
104 { 'Z', "System cancel" },
105 { 0, NULL }
106 };
107
108 static const value_string ouch_capacity_val[] = {
109 { 'A', "Agency" },
110 { 'O', "Other" },
111 { 'P', "Principal" },
112 { 'R', "Riskless" },
113 { 0, NULL }
114 };
115
116 static const value_string ouch_cross_type_val[] = {
117 { 'N', "No Cross" },
118 { 'O', "Opening Cross" },
119 { 'C', "Closing Cross" },
120 { 'I', "Intra-day Cross" }, /* Seems to have been removed */
121 { 'H', "Halt/IPO Cross" },
122 { 'R', "Retail" }, /* Not in 4.0 */
123 { 'S', "Supplemental Order" },
124 { 0, NULL }
125 };
126
127 /* Not in 4.0 */
128 static const value_string ouch_customer_type_val[] = {
129 { 'R', "Retail designated order" },
130 { 'N', "Not a retail designated order" },
131 { ' ', "Default configured for port" },
132 { 0, NULL }
133 };
134
135 static const value_string ouch_display_val[] = {
136 { 'A', "Attributable-Price to Display" },
137 { 'I', "Imbalance-Only" },
138 { 'L', "Post-Only and Attributable - Price to Display" },
139 { 'M', "Mid-Point Peg" },
140 { 'N', "Non-Display" },
141 { 'O', "Retail Order Type 1" }, /* Not in 4.0 */
142 { 'P', "Post-Only" },
143 { 'Q', "Retail Price Improvement Order" }, /* Not in 4.0 */
144 { 'R', "Round-Lot Only" }, /* Seems to have been removed? */
145 { 'T', "Retail Order Type 2" }, /* Not in 4.0 */
146 { 'W', "Mid-point Peg Post Only" },
147 { 'Y', "Anonymous-Price to Comply" },
148 { 'Z', "Entered as displayed bu changed to non-displayed "
149 "(Priced to comply)" }, /* New in 4.2 */
150 { 0, NULL}
151 };
152
153 static const value_string ouch_event_code_val[] = {
154 { 'S', "Start of Day" },
155 { 'E', "End of Day" },
156 { 0, NULL}
157 };
158
159 static const value_string ouch_iso_eligibility_val[] = {
160 { 'Y', "Eligible" },
161 { 'N', "Not eligible" },
162 { 0, NULL }
163 };
164
165 static const value_string ouch_liquidity_flag_val[] = {
166 { '0', "Supplemental Order Execution" },
167 { '4', "Added displayed liquidity in a Group A Symbol" },
168 { '5', "Added non-displayed liquidity in a Group A Symbol" },
169 { '6', "Removed liquidity in a Group A Symbol" },
170 { '7', "Displayed, liquidity-adding order improves the NBBO" },
171 { '8', "Displayed, liquidity-adding order sets the QBBO while joining the NBBO" },
172 { 'A', "Added" },
173 { 'C', "Closing Cross" },
174 { 'H', "Halt/IPO Cross" },
175 { 'I', "Intraday/Post-Market Cross" }, /* Seems to have been removed */
176 { 'J', "Non-displayed adding liquidity" },
177 { 'K', "Halt Cross" },
178 { 'L', "Closing Cross (imbalance-only)" },
179 { 'M', "Opening Cross (imbalance-only)" },
180 { 'N', "Halt Cross, orders entered in pilot symbols during the LULD Trading Pause" },
181 { 'O', "Opening Cross" },
182 { 'R', "Removed" },
183 { 'W', "Added post-only" }, /* Removed 4.2 2013/02/05 */
184 { 'a', "Added displayed liquidity in a SCIP Symbol" },
185 { 'b', "Displayed, liquidity-adding order improves the NBBO in pilot symbol during specified LULD Pricing Pilot timeframe" },
186 { 'c', "Added displayed liquidity in a pilot symbol during specified LULD Pricing Pilot timeframe" },
187 { 'd', "Retail designated execution that removed liquidity" },
188 { 'e', "Retail designated execution that added displayed liquidity" },
189 { 'f', "Retail designated execution that added non-displayed liquidity" },
190 { 'g', "Added non-displayed mid-point liquidity in a Group A Symbol" },
191 { 'h', "Removed liquidity in a pilot symbol during specified LULD Pricing Pilot timeframe" },
192 { 'j', "RPI (Retail Price Improving) order provides liquidity" },
193 { 'k', "Added liquidity via a midpoint order" },
194 { 'm', "Removed liquidity at a midpoint" },
195 { 'r', "Retail Order removes RPI liquidity" },
196 { 't', "Retail Order removes price improving non-displayed liquidity other than RPI liquidity" },
197 { 'x', "Displayed, liquidity-adding order improves the NBBO in a SCIP Symbol" },
198 { 'y', "Displayed, liquidity-adding order set the QBBO while joining the NBBO in a SCIP Symbol" },
199 { 0, NULL }
200 };
201
202 static const value_string ouch_order_state_val[] = {
203 { 'L', "Order Live" },
204 { 'D', "Order Dead" },
205 { 0, NULL }
206 };
207
208 static const value_string ouch_price_correction_reason_val[] = {
209 { 'E', "Erroneous" },
210 { 'C', "Consent" },
211 { 'S', "Supervisory" },
212 { 'X', "External" },
213 { 0, NULL }
214 };
215
216 static const value_string ouch_reference_price_type_val[] = {
217 { 'I', "Intraday Indicative Value" },
218 { 0, NULL }
219 };
220
221 static const value_string ouch_reject_reason_val[] = {
222 { 'T', "Test Mode" },
223 { 'H', "Halted" },
224 { 'Z', "Shares exceeds configured safety threshold" },
225 { 'S', "Invalid Stock" },
226 { 'D', "Invalid Display Type" },
227 { 'C', "NASDAQ is Closed" },
228 { 'L', "Requested firm not authorized for requested clearing "
229 "type on this account" },
230 { 'M', "Outside of permitted times for requested clearing type" },
231 { 'R', "This order is not allowed in this type of cross" },
232 { 'X', "Invalid Price" },
233 { 'N', "Invalid Minimum Quantity" },
234 { 'O', "Other" },
235 { 'W', "Invalid Mid-point Post Only Price" },
236 { 'a', "Reject All enabled" },
237 { 'b', "Easy to Borrow (ETB) reject" },
238 { 'c', "Restricted symbol list reject" },
239 { 'd', "ISO order restriction" },
240 { 'e', "Odd lot order restriction" },
241 { 'f', "Mid-Point order restriction" },
242 { 'g', "Pre-market order restriction" },
243 { 'h', "Post-market order restriction" },
244 { 'i', "Short sale order restriction" },
245 { 'j', "On Open order restriction" },
246 { 'k', "On Close order restriction" },
247 { 'l', "Two sided quote reject" },
248 { 'm', "Exceeded shares limit" },
249 { 'n', "Exceeded dollar value limit" },
250 { 0, NULL}
251 };
252
253 static const value_string ouch_trade_correction_reason_val[] = {
254 { 'N', "Adjusted to NAV" },
255 { 0, NULL }
256 };
257
258
259 /* Initialize the protocol and registered fields */
260 static int proto_ouch = -1;
261 static dissector_handle_t ouch_handle;
262
263 /* Initialize the subtree pointers */
264 static gint ett_ouch = -1;
265
266 static int hf_ouch_bbo_weight_indicator = -1;
267 static int hf_ouch_broken_trade_reason = -1;
268 static int hf_ouch_buy_sell_indicator = -1;
269 static int hf_ouch_cancel_reason = -1;
270 static int hf_ouch_capacity = -1;
271 static int hf_ouch_cross_type = -1;
272 static int hf_ouch_customer_type = -1;
273 static int hf_ouch_decrement_shares = -1;
274 static int hf_ouch_display = -1;
275 static int hf_ouch_event_code = -1;
276 static int hf_ouch_executed_shares = -1;
277 static int hf_ouch_execution_price = -1;
278 static int hf_ouch_existing_order_token = -1;
279 static int hf_ouch_firm = -1;
280 static int hf_ouch_iso_eligible = -1;
281 static int hf_ouch_liquidity_flag = -1;
282 static int hf_ouch_match_number = -1;
283 static int hf_ouch_message = -1;
284 static int hf_ouch_min_quantity = -1;
285 static int hf_ouch_new_execution_price = -1;
286 static int hf_ouch_order_reference_number = -1;
287 static int hf_ouch_order_state = -1;
288 static int hf_ouch_order_token = -1;
289 static int hf_ouch_packet_type = -1;
290 static int hf_ouch_previous_order_token = -1;
291 static int hf_ouch_price = -1;
292 static int hf_ouch_price_correction_reason = -1;
293 static int hf_ouch_quantity_prevented_from_trading = -1;
294 static int hf_ouch_reference_price = -1;
295 static int hf_ouch_reference_price_type = -1;
296 static int hf_ouch_reject_reason = -1;
297 static int hf_ouch_replacement_order_token = -1;
298 static int hf_ouch_shares = -1;
299 static int hf_ouch_stock = -1;
300 static int hf_ouch_tif = -1;
301 static int hf_ouch_timestamp = -1;
302 static int hf_ouch_trade_correction_reason = -1;
303
304
305 /** Format an OUCH timestamp into a useful string
306 *
307 * We use this function rather than a BASE_CUSTOM formatter because
308 * BASE_CUSTOM doesn't support passing a 64-bit value to the
309 * formatting function. */
310 static void
ouch_tree_add_timestamp(proto_tree * tree,const int hf,tvbuff_t * tvb,gint offset)311 ouch_tree_add_timestamp(
312 proto_tree *tree,
313 const int hf,
314 tvbuff_t *tvb,
315 gint offset)
316 {
317 guint64 ts = tvb_get_ntoh64(tvb, offset);
318 char *buf = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH);
319 guint32 tmp, hours, mins, secs, nsecs;
320
321 nsecs = (guint32)(ts % G_GUINT64_CONSTANT(1000000000));
322 tmp = (guint32)(ts / G_GUINT64_CONSTANT(1000000000));
323
324 hours = tmp / 3600;
325 mins = (tmp % 3600) / 60;
326 secs = tmp % 60;
327
328 g_snprintf(buf, ITEM_LABEL_LENGTH,
329 "%u:%02u:%02u.%09u",
330 hours, mins, secs, nsecs);
331
332 proto_tree_add_string(tree, hf, tvb, offset, 8, buf);
333 }
334
335 /** BASE_CUSTOM formatter for prices
336 *
337 * OUCH prices are integers, with four implicit decimal places. So we
338 * insert the decimal point, and add a leading dollar sign as well. */
339 static void
format_price(char * buf,guint32 value)340 format_price(
341 char *buf,
342 guint32 value)
343 {
344 if (value == 0x7fffffff) {
345 g_snprintf(buf, ITEM_LABEL_LENGTH, "%s", "Market");
346 } else {
347 g_snprintf(buf, ITEM_LABEL_LENGTH,
348 "$%u.%04u",
349 value / 10000, value % 10000);
350 }
351 }
352
353 /** BASE_CUSTOM formatter for reference price type code
354 *
355 * Displays the code value as a character, not its ASCII value, as
356 * would be done by BASE_DEC and friends. */
357 static void
format_reference_price_type(char * buf,guint32 value)358 format_reference_price_type(
359 char *buf,
360 guint32 value)
361 {
362 g_snprintf(buf, ITEM_LABEL_LENGTH,
363 "%s (%c)",
364 val_to_str_const(value,
365 ouch_reference_price_type_val,
366 "Unknown"),
367 value);
368 }
369
370 /** BASE_CUSTOM formatter for the Time In Force (TIF) code
371 *
372 * There are three reserved values for the TIF: 0, 99998 and 99999.
373 * These are trapped and displayed as an appropriate string. All
374 * other values are printed as a duration in hours, minutes and
375 * seconds. */
376 static void
format_tif(gchar * buf,guint32 value)377 format_tif(
378 gchar *buf,
379 guint32 value)
380 {
381 guint32 hours;
382 guint32 mins;
383 guint32 secs;
384
385 switch (value) {
386 case 0:
387 g_snprintf(buf, ITEM_LABEL_LENGTH, "Immediate Or Cancel (%u)", value);
388 break;
389
390 case 99998:
391 g_snprintf(buf, ITEM_LABEL_LENGTH, "Market Hours (%u)", value);
392 break;
393
394 case 99999:
395 g_snprintf(buf, ITEM_LABEL_LENGTH, "System Hours (%u)", value);
396 break;
397
398 default:
399 hours = value / 3600;
400 mins = (value % 3600) / 60;
401 secs = value % 60;
402
403 g_snprintf(buf, ITEM_LABEL_LENGTH,
404 "%uh %02um %02us (%u seconds)",
405 hours, mins, secs,
406 value);
407 break;
408 }
409 }
410
411
412 static int
dissect_ouch(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)413 dissect_ouch(
414 tvbuff_t *tvb,
415 packet_info *pinfo,
416 proto_tree *tree,
417 void *data _U_)
418 {
419 proto_item *ti;
420 proto_tree *ouch_tree = NULL;
421 const char *pkt_name;
422 guint16 reported_len;
423 guint8 pkt_type;
424 int offset = 0;
425
426 /* Get the OUCH message type value */
427 pkt_type = tvb_get_guint8(tvb, offset);
428 reported_len = tvb_reported_length(tvb);
429
430 /* OUCH has two messages with the same code: Replace Order and
431 * Replaced. It's possible to tell which is which because clients
432 * send the Replace Order, and NASDAQ sends Replaced replies.
433 * Nonetheless, this complicates the switch, so instead we
434 * distinguish between them by length, and use 'R' for Replaced
435 * (like XPRS does). */
436 if (pkt_type == 'U' && (reported_len == 79 || reported_len == 80)) {
437 pkt_type = 'R';
438 }
439
440 /* OUCH has two messages with the same code: Modify Order and
441 * Modified. Again, one is sent by clients, the other sent by
442 * NASDAQ. We change Modified to 'm' for simplicity in the
443 * switch. */
444 if (pkt_type == 'M' && reported_len == 28) {
445 pkt_type = 'm';
446 }
447
448 /* Since we use the packet name a few times, get and save that value */
449 pkt_name = val_to_str(pkt_type, pkt_type_val, "Unknown (%u)");
450
451 /* Set the protocol name in the summary display */
452 col_set_str(pinfo->cinfo, COL_PROTOCOL, "OUCH");
453
454 /* Set the packet name in the info column */
455 col_add_str(pinfo->cinfo, COL_INFO, pkt_name);
456
457 if (tree) {
458 /* Create a sub-tree for the OUCH packet details */
459 ti = proto_tree_add_item(tree,
460 proto_ouch,
461 tvb, 0, -1, ENC_NA);
462
463 ouch_tree = proto_item_add_subtree(ti, ett_ouch);
464
465 /* Append the packet name to the sub-tree item */
466 proto_item_append_text(ti, ", %s", pkt_name);
467
468 /* Packet type (using the cooked value). */
469 proto_tree_add_item(ouch_tree, hf_ouch_packet_type,
470 tvb, offset, 1, ENC_ASCII|ENC_NA);
471 offset += 1;
472
473 switch (pkt_type) {
474 case 'O': /* Enter Order */
475 proto_tree_add_item(ouch_tree,
476 hf_ouch_order_token,
477 tvb, offset, 14,
478 ENC_ASCII|ENC_NA);
479 offset += 14;
480
481 proto_tree_add_item(ouch_tree,
482 hf_ouch_buy_sell_indicator,
483 tvb, offset, 1,
484 ENC_ASCII|ENC_NA);
485 offset += 1;
486
487 proto_tree_add_item(ouch_tree,
488 hf_ouch_shares,
489 tvb, offset, 4,
490 ENC_BIG_ENDIAN);
491 offset += 4;
492
493 proto_tree_add_item(ouch_tree,
494 hf_ouch_stock,
495 tvb, offset, 8,
496 ENC_ASCII|ENC_NA);
497 offset += 8;
498
499 proto_tree_add_item(ouch_tree,
500 hf_ouch_price,
501 tvb, offset, 4,
502 ENC_BIG_ENDIAN);
503 offset += 4;
504
505 proto_tree_add_item(ouch_tree,
506 hf_ouch_tif,
507 tvb, offset, 4,
508 ENC_BIG_ENDIAN);
509 offset += 4;
510
511 proto_tree_add_item(ouch_tree,
512 hf_ouch_firm,
513 tvb, offset, 4,
514 ENC_ASCII|ENC_NA);
515 offset += 4;
516
517 proto_tree_add_item(ouch_tree,
518 hf_ouch_display,
519 tvb, offset, 1,
520 ENC_ASCII|ENC_NA);
521 offset += 1;
522
523 proto_tree_add_item(ouch_tree,
524 hf_ouch_capacity,
525 tvb, offset, 1,
526 ENC_ASCII|ENC_NA);
527 offset += 1;
528
529 proto_tree_add_item(ouch_tree,
530 hf_ouch_iso_eligible,
531 tvb, offset, 1,
532 ENC_ASCII|ENC_NA);
533 offset += 1;
534
535 proto_tree_add_item(ouch_tree,
536 hf_ouch_min_quantity,
537 tvb, offset, 4,
538 ENC_BIG_ENDIAN);
539 offset += 4;
540
541 proto_tree_add_item(ouch_tree,
542 hf_ouch_cross_type,
543 tvb, offset, 1,
544 ENC_ASCII|ENC_NA);
545 offset += 1;
546
547 if (reported_len >= 49) { /* Added in 4.1 */
548 proto_tree_add_item(ouch_tree,
549 hf_ouch_customer_type,
550 tvb, offset, 1,
551 ENC_ASCII|ENC_NA);
552 offset += 1;
553 }
554 break;
555
556 case 'A': /* Accepted */
557 ouch_tree_add_timestamp(ouch_tree,
558 hf_ouch_timestamp,
559 tvb, offset);
560 offset += 8;
561
562 proto_tree_add_item(ouch_tree,
563 hf_ouch_order_token,
564 tvb, offset, 14,
565 ENC_ASCII|ENC_NA);
566 offset += 14;
567
568 proto_tree_add_item(ouch_tree,
569 hf_ouch_buy_sell_indicator,
570 tvb, offset, 1,
571 ENC_BIG_ENDIAN);
572 offset += 1;
573
574 proto_tree_add_item(ouch_tree,
575 hf_ouch_shares,
576 tvb, offset, 4,
577 ENC_BIG_ENDIAN);
578 offset += 4;
579
580 proto_tree_add_item(ouch_tree,
581 hf_ouch_stock,
582 tvb, offset, 8,
583 ENC_ASCII|ENC_NA);
584 offset += 8;
585
586 proto_tree_add_item(ouch_tree,
587 hf_ouch_price,
588 tvb, offset, 4,
589 ENC_BIG_ENDIAN);
590 offset += 4;
591
592 proto_tree_add_item(ouch_tree,
593 hf_ouch_tif,
594 tvb, offset, 4,
595 ENC_BIG_ENDIAN);
596 offset += 4;
597
598 proto_tree_add_item(ouch_tree,
599 hf_ouch_firm,
600 tvb, offset, 4,
601 ENC_ASCII|ENC_NA);
602 offset += 4;
603
604 proto_tree_add_item(ouch_tree,
605 hf_ouch_display,
606 tvb, offset, 1,
607 ENC_ASCII|ENC_NA);
608 offset += 1;
609
610 proto_tree_add_item(ouch_tree,
611 hf_ouch_order_reference_number,
612 tvb, offset, 8,
613 ENC_BIG_ENDIAN);
614 offset += 8;
615
616 proto_tree_add_item(ouch_tree,
617 hf_ouch_capacity,
618 tvb, offset, 1,
619 ENC_ASCII|ENC_NA);
620 offset += 1;
621
622 proto_tree_add_item(ouch_tree,
623 hf_ouch_iso_eligible,
624 tvb, offset, 1,
625 ENC_ASCII|ENC_NA);
626 offset += 1;
627
628 proto_tree_add_item(ouch_tree,
629 hf_ouch_min_quantity,
630 tvb, offset, 4,
631 ENC_BIG_ENDIAN);
632 offset += 4;
633
634 proto_tree_add_item(ouch_tree,
635 hf_ouch_cross_type,
636 tvb, offset, 1,
637 ENC_ASCII|ENC_NA);
638 offset += 1;
639
640 proto_tree_add_item(ouch_tree,
641 hf_ouch_order_state,
642 tvb, offset, 1,
643 ENC_ASCII|ENC_NA);
644 offset += 1;
645
646 if (reported_len >= 66) { /* Added in 4.2 */
647 proto_tree_add_item(ouch_tree,
648 hf_ouch_bbo_weight_indicator,
649 tvb, offset, 1,
650 ENC_ASCII|ENC_NA);
651 offset += 1;
652 }
653 break;
654
655 case 'U': /* Replace Order */
656 proto_tree_add_item(ouch_tree,
657 hf_ouch_existing_order_token,
658 tvb, offset, 14,
659 ENC_ASCII|ENC_NA);
660 offset += 14;
661
662 proto_tree_add_item(ouch_tree,
663 hf_ouch_replacement_order_token,
664 tvb, offset, 14,
665 ENC_ASCII|ENC_NA);
666 offset += 14;
667
668 proto_tree_add_item(ouch_tree,
669 hf_ouch_shares,
670 tvb, offset, 4,
671 ENC_BIG_ENDIAN);
672 offset += 4;
673
674 proto_tree_add_item(ouch_tree,
675 hf_ouch_price,
676 tvb, offset, 4,
677 ENC_BIG_ENDIAN);
678 offset += 4;
679
680 proto_tree_add_item(ouch_tree,
681 hf_ouch_tif,
682 tvb, offset, 4,
683 ENC_BIG_ENDIAN);
684 offset += 4;
685
686 proto_tree_add_item(ouch_tree,
687 hf_ouch_display,
688 tvb, offset, 1,
689 ENC_ASCII|ENC_NA);
690 offset += 1;
691
692 proto_tree_add_item(ouch_tree,
693 hf_ouch_iso_eligible,
694 tvb, offset, 1,
695 ENC_ASCII|ENC_NA);
696 offset += 1;
697
698 proto_tree_add_item(ouch_tree,
699 hf_ouch_min_quantity,
700 tvb, offset, 4,
701 ENC_BIG_ENDIAN);
702 offset += 4;
703 break;
704
705 case 'X': /* Cancel Order */
706 proto_tree_add_item(ouch_tree,
707 hf_ouch_order_token,
708 tvb, offset, 14,
709 ENC_ASCII|ENC_NA);
710 offset += 14;
711
712 proto_tree_add_item(ouch_tree,
713 hf_ouch_shares,
714 tvb, offset, 4,
715 ENC_BIG_ENDIAN);
716 offset += 4;
717 break;
718
719 case 'M': /* Modify Order (from 4.2 onwards) */
720 proto_tree_add_item(ouch_tree,
721 hf_ouch_order_token,
722 tvb, offset, 14,
723 ENC_ASCII|ENC_NA);
724 offset += 14;
725
726 proto_tree_add_item(ouch_tree,
727 hf_ouch_buy_sell_indicator,
728 tvb, offset, 1,
729 ENC_BIG_ENDIAN);
730 offset += 1;
731
732 proto_tree_add_item(ouch_tree,
733 hf_ouch_shares,
734 tvb, offset, 4,
735 ENC_BIG_ENDIAN);
736 offset += 4;
737 break;
738
739 case 'S': /* System Event */
740 ouch_tree_add_timestamp(ouch_tree,
741 hf_ouch_timestamp,
742 tvb, offset);
743 offset += 8;
744
745 proto_tree_add_item(ouch_tree,
746 hf_ouch_event_code,
747 tvb, offset, 1,
748 ENC_ASCII|ENC_NA);
749 offset += 1;
750 break;
751
752 case 'R': /* Replaced */
753 ouch_tree_add_timestamp(ouch_tree,
754 hf_ouch_timestamp,
755 tvb, offset);
756 offset += 8;
757
758 proto_tree_add_item(ouch_tree,
759 hf_ouch_replacement_order_token,
760 tvb, offset, 14,
761 ENC_ASCII|ENC_NA);
762 offset += 14;
763
764 proto_tree_add_item(ouch_tree,
765 hf_ouch_buy_sell_indicator,
766 tvb, offset, 1,
767 ENC_BIG_ENDIAN);
768 offset += 1;
769
770 proto_tree_add_item(ouch_tree,
771 hf_ouch_shares,
772 tvb, offset, 4,
773 ENC_BIG_ENDIAN);
774 offset += 4;
775
776 proto_tree_add_item(ouch_tree,
777 hf_ouch_stock,
778 tvb, offset, 8,
779 ENC_ASCII|ENC_NA);
780 offset += 8;
781
782 proto_tree_add_item(ouch_tree,
783 hf_ouch_price,
784 tvb, offset, 4,
785 ENC_BIG_ENDIAN);
786 offset += 4;
787
788 proto_tree_add_item(ouch_tree,
789 hf_ouch_tif,
790 tvb, offset, 4,
791 ENC_BIG_ENDIAN);
792 offset += 4;
793
794 proto_tree_add_item(ouch_tree,
795 hf_ouch_firm,
796 tvb, offset, 4,
797 ENC_ASCII|ENC_NA);
798 offset += 4;
799
800 proto_tree_add_item(ouch_tree,
801 hf_ouch_display,
802 tvb, offset, 1,
803 ENC_ASCII|ENC_NA);
804 offset += 1;
805
806 proto_tree_add_item(ouch_tree,
807 hf_ouch_order_reference_number,
808 tvb, offset, 8,
809 ENC_BIG_ENDIAN);
810 offset += 8;
811
812 proto_tree_add_item(ouch_tree,
813 hf_ouch_capacity,
814 tvb, offset, 1,
815 ENC_ASCII|ENC_NA);
816 offset += 1;
817
818 proto_tree_add_item(ouch_tree,
819 hf_ouch_iso_eligible,
820 tvb, offset, 1,
821 ENC_ASCII|ENC_NA);
822 offset += 1;
823
824 proto_tree_add_item(ouch_tree,
825 hf_ouch_min_quantity,
826 tvb, offset, 4,
827 ENC_BIG_ENDIAN);
828 offset += 4;
829
830 proto_tree_add_item(ouch_tree,
831 hf_ouch_cross_type,
832 tvb, offset, 1,
833 ENC_ASCII|ENC_NA);
834 offset += 1;
835
836 proto_tree_add_item(ouch_tree,
837 hf_ouch_order_state,
838 tvb, offset, 1,
839 ENC_ASCII|ENC_NA);
840 offset += 1;
841
842 proto_tree_add_item(ouch_tree,
843 hf_ouch_previous_order_token,
844 tvb, offset, 14,
845 ENC_ASCII|ENC_NA);
846 offset += 14;
847
848 if (reported_len >= 80) { /* Added in 4.2 */
849 proto_tree_add_item(ouch_tree,
850 hf_ouch_bbo_weight_indicator,
851 tvb, offset, 1,
852 ENC_BIG_ENDIAN);
853 offset += 1;
854 }
855 break;
856
857 case 'C': /* Canceled */
858 ouch_tree_add_timestamp(ouch_tree,
859 hf_ouch_timestamp,
860 tvb, offset);
861 offset += 8;
862
863 proto_tree_add_item(ouch_tree,
864 hf_ouch_order_token,
865 tvb, offset, 14,
866 ENC_ASCII|ENC_NA);
867 offset += 14;
868
869 proto_tree_add_item(ouch_tree,
870 hf_ouch_decrement_shares,
871 tvb, offset, 4,
872 ENC_BIG_ENDIAN);
873 offset += 4;
874
875 proto_tree_add_item(ouch_tree,
876 hf_ouch_cancel_reason,
877 tvb, offset, 1,
878 ENC_ASCII|ENC_NA);
879 offset += 1;
880 break;
881
882 case 'D': /* AIQ Canceled */
883 ouch_tree_add_timestamp(ouch_tree,
884 hf_ouch_timestamp,
885 tvb, offset);
886 offset += 8;
887
888 proto_tree_add_item(ouch_tree,
889 hf_ouch_order_token,
890 tvb, offset, 14,
891 ENC_ASCII|ENC_NA);
892 offset += 14;
893
894 proto_tree_add_item(ouch_tree,
895 hf_ouch_decrement_shares,
896 tvb, offset, 4,
897 ENC_BIG_ENDIAN);
898 offset += 4;
899
900 proto_tree_add_item(ouch_tree,
901 hf_ouch_cancel_reason,
902 tvb, offset, 1,
903 ENC_ASCII|ENC_NA);
904 offset += 1;
905
906 proto_tree_add_item(ouch_tree,
907 hf_ouch_quantity_prevented_from_trading,
908 tvb, offset, 4,
909 ENC_BIG_ENDIAN);
910 offset += 4;
911
912 proto_tree_add_item(ouch_tree,
913 hf_ouch_execution_price,
914 tvb, offset, 4,
915 ENC_BIG_ENDIAN);
916 offset += 4;
917
918 proto_tree_add_item(ouch_tree,
919 hf_ouch_liquidity_flag,
920 tvb, offset, 1,
921 ENC_ASCII|ENC_NA);
922 offset += 1;
923 break;
924
925 case 'E': /* Executed */
926 ouch_tree_add_timestamp(ouch_tree,
927 hf_ouch_timestamp,
928 tvb, offset);
929 offset += 8;
930
931 proto_tree_add_item(ouch_tree,
932 hf_ouch_order_token,
933 tvb, offset, 14,
934 ENC_ASCII|ENC_NA);
935 offset += 14;
936
937 proto_tree_add_item(ouch_tree,
938 hf_ouch_executed_shares,
939 tvb, offset, 4,
940 ENC_BIG_ENDIAN);
941 offset += 4;
942
943 proto_tree_add_item(ouch_tree,
944 hf_ouch_execution_price,
945 tvb, offset, 4,
946 ENC_BIG_ENDIAN);
947 offset += 4;
948
949 proto_tree_add_item(ouch_tree,
950 hf_ouch_liquidity_flag,
951 tvb, offset, 1,
952 ENC_ASCII|ENC_NA);
953 offset += 1;
954
955 proto_tree_add_item(ouch_tree,
956 hf_ouch_match_number,
957 tvb, offset, 8,
958 ENC_BIG_ENDIAN);
959 offset += 8;
960 break;
961
962 case 'B': /* Broken Trade */
963 ouch_tree_add_timestamp(ouch_tree,
964 hf_ouch_timestamp,
965 tvb, offset);
966 offset += 8;
967
968 proto_tree_add_item(ouch_tree,
969 hf_ouch_order_token,
970 tvb, offset, 14,
971 ENC_ASCII|ENC_NA);
972 offset += 14;
973
974 proto_tree_add_item(ouch_tree,
975 hf_ouch_match_number,
976 tvb, offset, 8,
977 ENC_BIG_ENDIAN);
978 offset += 8;
979
980 proto_tree_add_item(ouch_tree,
981 hf_ouch_broken_trade_reason,
982 tvb, offset, 1,
983 ENC_ASCII|ENC_NA);
984 offset += 1;
985 break;
986
987 case 'F': /* Trade Correction (4.2 onwards) */
988 ouch_tree_add_timestamp(ouch_tree,
989 hf_ouch_timestamp,
990 tvb, offset);
991 offset += 8;
992
993 proto_tree_add_item(ouch_tree,
994 hf_ouch_order_token,
995 tvb, offset, 14,
996 ENC_ASCII|ENC_NA);
997 offset += 14;
998
999 proto_tree_add_item(ouch_tree,
1000 hf_ouch_executed_shares,
1001 tvb, offset, 4,
1002 ENC_BIG_ENDIAN);
1003 offset += 4;
1004
1005 proto_tree_add_item(ouch_tree,
1006 hf_ouch_execution_price,
1007 tvb, offset, 4,
1008 ENC_BIG_ENDIAN);
1009 offset += 4;
1010
1011 proto_tree_add_item(ouch_tree,
1012 hf_ouch_liquidity_flag,
1013 tvb, offset, 1,
1014 ENC_ASCII|ENC_NA);
1015 offset += 1;
1016
1017 proto_tree_add_item(ouch_tree,
1018 hf_ouch_match_number,
1019 tvb, offset, 8,
1020 ENC_BIG_ENDIAN);
1021 offset += 8;
1022
1023 proto_tree_add_item(ouch_tree,
1024 hf_ouch_trade_correction_reason,
1025 tvb, offset, 1,
1026 ENC_ASCII|ENC_NA);
1027 offset += 1;
1028 break;
1029
1030 case 'G': /* Executed with Reference Price (4.2 onwards) */
1031 ouch_tree_add_timestamp(ouch_tree,
1032 hf_ouch_timestamp,
1033 tvb, offset);
1034 offset += 8;
1035
1036 proto_tree_add_item(ouch_tree,
1037 hf_ouch_order_token,
1038 tvb, offset, 14,
1039 ENC_ASCII|ENC_NA);
1040 offset += 14;
1041
1042 proto_tree_add_item(ouch_tree,
1043 hf_ouch_executed_shares,
1044 tvb, offset, 4,
1045 ENC_BIG_ENDIAN);
1046 offset += 4;
1047
1048 proto_tree_add_item(ouch_tree,
1049 hf_ouch_execution_price,
1050 tvb, offset, 4,
1051 ENC_BIG_ENDIAN);
1052 offset += 4;
1053
1054 proto_tree_add_item(ouch_tree,
1055 hf_ouch_liquidity_flag,
1056 tvb, offset, 1,
1057 ENC_ASCII|ENC_NA);
1058 offset += 1;
1059
1060 proto_tree_add_item(ouch_tree,
1061 hf_ouch_match_number,
1062 tvb, offset, 8,
1063 ENC_BIG_ENDIAN);
1064 offset += 8;
1065
1066 proto_tree_add_item(ouch_tree,
1067 hf_ouch_reference_price,
1068 tvb, offset, 4,
1069 ENC_BIG_ENDIAN);
1070 offset += 4;
1071
1072 proto_tree_add_item(ouch_tree,
1073 hf_ouch_reference_price_type,
1074 tvb, offset, 1,
1075 ENC_BIG_ENDIAN);
1076 offset += 1;
1077 break;
1078
1079 case 'K': /* Price Correction */
1080 ouch_tree_add_timestamp(ouch_tree,
1081 hf_ouch_timestamp,
1082 tvb, offset);
1083 offset += 8;
1084
1085 proto_tree_add_item(ouch_tree,
1086 hf_ouch_order_token,
1087 tvb, offset, 14,
1088 ENC_ASCII|ENC_NA);
1089 offset += 14;
1090
1091 proto_tree_add_item(ouch_tree,
1092 hf_ouch_match_number,
1093 tvb, offset, 8,
1094 ENC_BIG_ENDIAN);
1095 offset += 8;
1096
1097 proto_tree_add_item(ouch_tree,
1098 hf_ouch_new_execution_price,
1099 tvb, offset, 4,
1100 ENC_BIG_ENDIAN);
1101 offset += 4;
1102
1103 proto_tree_add_item(ouch_tree,
1104 hf_ouch_price_correction_reason,
1105 tvb, offset, 1,
1106 ENC_ASCII|ENC_NA);
1107 offset += 1;
1108 break;
1109
1110 case 'J': /* Rejected Order */
1111 ouch_tree_add_timestamp(ouch_tree,
1112 hf_ouch_timestamp,
1113 tvb, offset);
1114 offset += 8;
1115
1116 proto_tree_add_item(ouch_tree,
1117 hf_ouch_order_token,
1118 tvb, offset, 14,
1119 ENC_ASCII|ENC_NA);
1120 offset += 14;
1121
1122 proto_tree_add_item(ouch_tree,
1123 hf_ouch_reject_reason,
1124 tvb, offset, 1,
1125 ENC_ASCII|ENC_NA);
1126 offset += 1;
1127 break;
1128
1129 case 'P': /* Cancel Pending */
1130 ouch_tree_add_timestamp(ouch_tree,
1131 hf_ouch_timestamp,
1132 tvb, offset);
1133 offset += 8;
1134
1135 proto_tree_add_item(ouch_tree,
1136 hf_ouch_order_token,
1137 tvb, offset, 14,
1138 ENC_ASCII|ENC_NA);
1139 offset += 14;
1140 break;
1141
1142 case 'I': /* Cancel Reject */
1143 ouch_tree_add_timestamp(ouch_tree,
1144 hf_ouch_timestamp,
1145 tvb, offset);
1146 offset += 8;
1147
1148 proto_tree_add_item(ouch_tree,
1149 hf_ouch_order_token,
1150 tvb, offset, 14,
1151 ENC_ASCII|ENC_NA);
1152 offset += 14;
1153 break;
1154
1155 case 'T': /* Order Priority Update (4.2 onwards) */
1156 ouch_tree_add_timestamp(ouch_tree,
1157 hf_ouch_timestamp,
1158 tvb, offset);
1159 offset += 8;
1160
1161 proto_tree_add_item(ouch_tree,
1162 hf_ouch_order_token,
1163 tvb, offset, 14,
1164 ENC_ASCII|ENC_NA);
1165 offset += 14;
1166
1167 proto_tree_add_item(ouch_tree,
1168 hf_ouch_price,
1169 tvb, offset, 4,
1170 ENC_BIG_ENDIAN);
1171 offset += 4;
1172
1173 proto_tree_add_item(ouch_tree,
1174 hf_ouch_display,
1175 tvb, offset, 1,
1176 ENC_ASCII|ENC_NA);
1177 offset += 1;
1178
1179 proto_tree_add_item(ouch_tree,
1180 hf_ouch_order_reference_number,
1181 tvb, offset, 8,
1182 ENC_BIG_ENDIAN);
1183 offset += 8;
1184 break;
1185
1186 case 'm': /* Order Modified (4.2 onwards) */
1187 ouch_tree_add_timestamp(ouch_tree,
1188 hf_ouch_timestamp,
1189 tvb, offset);
1190 offset += 8;
1191
1192 proto_tree_add_item(ouch_tree,
1193 hf_ouch_order_token,
1194 tvb, offset, 14,
1195 ENC_ASCII|ENC_NA);
1196 offset += 14;
1197
1198 proto_tree_add_item(ouch_tree,
1199 hf_ouch_buy_sell_indicator,
1200 tvb, offset, 1,
1201 ENC_BIG_ENDIAN);
1202 offset += 1;
1203
1204 proto_tree_add_item(ouch_tree,
1205 hf_ouch_shares,
1206 tvb, offset, 4,
1207 ENC_BIG_ENDIAN);
1208 offset += 4;
1209 break;
1210
1211 default:
1212 /* Unknown */
1213 proto_tree_add_item(tree,
1214 hf_ouch_message,
1215 tvb, offset, -1, ENC_NA);
1216 offset += reported_len - 1;
1217 break;
1218 }
1219 }
1220
1221 return offset;
1222 }
1223
1224 /** Returns a guess if a packet is OUCH or not
1225 *
1226 * Since SOUP doesn't have a sub-protocol type flag, we have to use a
1227 * heuristic decision to determine if the contained protocol is OUCH
1228 * or ITCH (or something else entirely). We look at the message type
1229 * code, and since we know that we're being called from SOUP, we can
1230 * check the passed-in length too: if the type code and the length
1231 * match, we guess at OUCH. */
1232 static gboolean
dissect_ouch_heur(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)1233 dissect_ouch_heur(
1234 tvbuff_t *tvb,
1235 packet_info *pinfo,
1236 proto_tree *tree,
1237 void *data _U_)
1238 {
1239 guint8 msg_type = tvb_get_guint8(tvb, 0);
1240 guint msg_len = tvb_reported_length(tvb);
1241
1242 switch (msg_type) {
1243 case 'O': /* Enter order (with or without optional customer type) */
1244 if (msg_len != 48 && msg_len != 49) {
1245 return FALSE;
1246 }
1247 break;
1248
1249 case 'U': /* Replace order or Replaced (4.0, 4.1) or Replaced (4.2) */
1250 if (msg_len != 47 && msg_len != 79 && msg_len != 80) {
1251 return FALSE;
1252 }
1253 break;
1254
1255 case 'X': /* Cancel order */
1256 if (msg_len != 19) {
1257 return FALSE;
1258 }
1259 break;
1260
1261 case 'M': /* Modify Order or Order Modified (added 4.2) */
1262 if (msg_len != 20 && msg_len != 28) {
1263 return FALSE;
1264 }
1265 break;
1266
1267 case 'S': /* System event */
1268 if (msg_len != 10) {
1269 return FALSE;
1270 }
1271 break;
1272
1273 case 'A': /* Accepted */
1274 if (msg_len != 65 && msg_len != 66) {
1275 return FALSE;
1276 }
1277 break;
1278
1279 case 'C': /* Canceled */
1280 if (msg_len != 28) {
1281 return FALSE;
1282 }
1283 break;
1284
1285 case 'D': /* AIQ Canceled */
1286 if (msg_len != 37) {
1287 return FALSE;
1288 }
1289 break;
1290 case 'E': /* Executed */
1291 if (msg_len != 40) {
1292 return FALSE;
1293 }
1294 break;
1295
1296 case 'F': /* Trade Correction */
1297 if (msg_len != 41) {
1298 return FALSE;
1299 }
1300 break;
1301
1302 case 'G': /* Executed with Reference Price */
1303 if (msg_len != 45) {
1304 return FALSE;
1305 }
1306 break;
1307
1308 case 'B': /* Broken Trade */
1309 if (msg_len != 32) {
1310 return FALSE;
1311 }
1312 break;
1313
1314 case 'K': /* Correction */
1315 if (msg_len != 36) {
1316 return FALSE;
1317 }
1318 break;
1319
1320 case 'J': /* Rejected */
1321 if (msg_len != 24) {
1322 return FALSE;
1323 }
1324 break;
1325
1326 case 'P': /* Cancel Pending */
1327 if (msg_len != 23) {
1328 return FALSE;
1329 }
1330 break;
1331
1332 case 'I': /* Cancel Reject */
1333 if (msg_len != 23) {
1334 return FALSE;
1335 }
1336 break;
1337
1338 case 'T': /* Order Priority Update */
1339 if (msg_len != 36) {
1340 return FALSE;
1341 }
1342 break;
1343
1344 default:
1345 /* Not a known OUCH message code */
1346 return FALSE;
1347 }
1348
1349 /* Perform dissection of this (initial) packet */
1350 dissect_ouch(tvb, pinfo, tree, NULL);
1351
1352 return TRUE;
1353 }
1354
1355
1356 void
proto_register_ouch(void)1357 proto_register_ouch(void)
1358 {
1359 /* Setup list of header fields See Section 1.6.1 for details*/
1360 static hf_register_info hf[] = {
1361
1362 { &hf_ouch_bbo_weight_indicator,
1363 { "BBO Weight Indicator", "ouch.bbo_weight_indicator",
1364 FT_CHAR, BASE_HEX, VALS(ouch_bbo_weight_indicator_val), 0x0,
1365 NULL, HFILL }},
1366
1367 { &hf_ouch_broken_trade_reason,
1368 { "Broken Trade Reason", "ouch.broken_trade_reason",
1369 FT_CHAR, BASE_HEX, VALS(ouch_broken_trade_reason_val), 0x0,
1370 NULL, HFILL }},
1371
1372 { &hf_ouch_buy_sell_indicator,
1373 { "Buy/Sell Indicator", "ouch.buy_sell_indicator",
1374 FT_CHAR, BASE_HEX, VALS(ouch_buy_sell_indicator_val), 0x0,
1375 NULL, HFILL }},
1376
1377 { &hf_ouch_cancel_reason,
1378 { "Cancel Reason", "ouch.cancel_reason",
1379 FT_CHAR, BASE_HEX, VALS(ouch_cancel_reason_val), 0x0,
1380 NULL, HFILL }},
1381
1382 { &hf_ouch_capacity,
1383 { "Capacity", "ouch.capacity",
1384 FT_CHAR, BASE_HEX, VALS(ouch_capacity_val), 0x0,
1385 NULL, HFILL }},
1386
1387 { &hf_ouch_cross_type,
1388 { "Cross Type", "ouch.cross_type",
1389 FT_CHAR, BASE_HEX, VALS(ouch_cross_type_val), 0x0,
1390 NULL, HFILL }},
1391
1392 { &hf_ouch_customer_type,
1393 { "Customer Type", "ouch.customer_type",
1394 FT_CHAR, BASE_HEX, VALS(ouch_customer_type_val), 0x0,
1395 NULL, HFILL }},
1396
1397 { &hf_ouch_decrement_shares,
1398 { "Decrement Shares", "ouch.decrement_shares",
1399 FT_UINT32, BASE_DEC, NULL, 0x0,
1400 NULL, HFILL }},
1401
1402 { &hf_ouch_display,
1403 { "Display", "ouch.display",
1404 FT_CHAR, BASE_HEX, VALS(ouch_display_val), 0x0,
1405 NULL, HFILL }},
1406
1407 { &hf_ouch_event_code,
1408 { "Event Code", "ouch.event_code",
1409 FT_CHAR, BASE_HEX, VALS(ouch_event_code_val), 0x0,
1410 NULL, HFILL }},
1411
1412 { &hf_ouch_executed_shares,
1413 { "Executed Shares", "ouch.executed_shares",
1414 FT_UINT32, BASE_DEC, NULL, 0x0,
1415 NULL, HFILL }},
1416
1417 { &hf_ouch_execution_price,
1418 { "Execution Price", "ouch.execution_price",
1419 FT_UINT32, BASE_CUSTOM, CF_FUNC(format_price), 0x0,
1420 NULL, HFILL }},
1421
1422 { &hf_ouch_existing_order_token,
1423 { "Existing Order Token", "ouch.existing_order_token",
1424 FT_STRING, BASE_NONE, NULL, 0x0,
1425 NULL, HFILL }},
1426
1427 { &hf_ouch_firm,
1428 { "Firm", "ouch.firm",
1429 FT_STRING, BASE_NONE, NULL, 0x0,
1430 NULL, HFILL }},
1431
1432 { &hf_ouch_iso_eligible,
1433 { "Intermarket Sweep Eligibility", "ouch.iso_eligible",
1434 FT_CHAR, BASE_HEX, VALS(ouch_iso_eligibility_val), 0x0,
1435 NULL, HFILL }},
1436
1437 { &hf_ouch_liquidity_flag,
1438 { "Liquidity Flag", "ouch.liquidity_flag",
1439 FT_CHAR, BASE_HEX, VALS(ouch_liquidity_flag_val), 0x0,
1440 NULL, HFILL }},
1441
1442 { &hf_ouch_match_number,
1443 { "Match Number", "ouch.match_number",
1444 FT_UINT64, BASE_DEC, NULL, 0x0,
1445 NULL, HFILL }},
1446
1447 { &hf_ouch_message,
1448 { "Unknown Message", "ouch.unknown_message",
1449 FT_BYTES, BASE_NONE, NULL, 0x0,
1450 NULL, HFILL }},
1451
1452 { &hf_ouch_min_quantity,
1453 { "Minimum Quantity", "ouch.min_quantity",
1454 FT_UINT32, BASE_DEC, NULL, 0x0,
1455 NULL, HFILL }},
1456
1457 { &hf_ouch_new_execution_price,
1458 { "New Execution Price", "ouch.new_execution_price",
1459 FT_UINT32, BASE_CUSTOM, CF_FUNC(format_price), 0x0,
1460 NULL, HFILL }},
1461
1462 { &hf_ouch_order_reference_number,
1463 { "Order Reference Number", "ouch.order_reference_number",
1464 FT_UINT64, BASE_DEC, NULL, 0x0,
1465 NULL, HFILL }},
1466
1467 { &hf_ouch_order_state,
1468 { "Order State", "ouch.order_state",
1469 FT_CHAR, BASE_HEX, VALS(ouch_order_state_val), 0x0,
1470 NULL, HFILL }},
1471
1472 { &hf_ouch_order_token,
1473 { "Order Token", "ouch.order_token",
1474 FT_STRING, BASE_NONE, NULL, 0x0,
1475 NULL, HFILL }},
1476
1477 { &hf_ouch_packet_type,
1478 { "Packet Type", "ouch.packet_type",
1479 FT_CHAR, BASE_HEX, VALS(pkt_type_val), 0x0,
1480 NULL, HFILL }},
1481
1482 { &hf_ouch_previous_order_token,
1483 { "Previous Order Token", "ouch.previous_order_token",
1484 FT_STRING, BASE_NONE, NULL, 0x0,
1485 NULL, HFILL }},
1486
1487 { &hf_ouch_price,
1488 { "Price", "ouch.price",
1489 FT_UINT32, BASE_CUSTOM, CF_FUNC(format_price), 0x0,
1490 NULL, HFILL }},
1491
1492 { &hf_ouch_price_correction_reason,
1493 { "Price Correction Reason", "ouch.price_correction_reason",
1494 FT_CHAR, BASE_HEX, VALS(ouch_price_correction_reason_val), 0x0,
1495 NULL, HFILL }},
1496
1497 { &hf_ouch_quantity_prevented_from_trading,
1498 { "Quantity Prevented from Trading",
1499 "ouch.quantity_prevented_from_trading",
1500 FT_UINT32, BASE_DEC, NULL, 0x0,
1501 NULL, HFILL }},
1502
1503 { &hf_ouch_reference_price,
1504 { "Reference Price", "ouch.reference_price",
1505 FT_UINT32, BASE_CUSTOM, CF_FUNC(format_price), 0x0,
1506 NULL, HFILL }},
1507
1508 { &hf_ouch_reference_price_type,
1509 { "Reference Price Type", "ouch.reference_price_type",
1510 FT_UINT32, BASE_CUSTOM, CF_FUNC(format_reference_price_type), 0x0,
1511 NULL, HFILL }},
1512
1513 { &hf_ouch_reject_reason,
1514 { "Reject Reason", "ouch.reject_reason",
1515 FT_CHAR, BASE_HEX, VALS(ouch_reject_reason_val), 0x0,
1516 NULL, HFILL }},
1517
1518 { &hf_ouch_replacement_order_token,
1519 { "Replacement Order Token", "ouch.replacement_order_token",
1520 FT_STRING, BASE_NONE, NULL, 0x0,
1521 NULL, HFILL }},
1522
1523 { &hf_ouch_shares,
1524 { "Shares", "ouch.shares",
1525 FT_UINT32, BASE_DEC, NULL, 0x0,
1526 NULL, HFILL }},
1527
1528 { &hf_ouch_stock,
1529 { "Stock", "ouch.stock",
1530 FT_STRING, BASE_NONE, NULL, 0x0,
1531 NULL, HFILL }},
1532
1533 { &hf_ouch_tif,
1534 { "Time In Force", "ouch.tif",
1535 FT_UINT32, BASE_CUSTOM, CF_FUNC(format_tif), 0x0,
1536 NULL, HFILL }},
1537
1538 { &hf_ouch_timestamp,
1539 { "Timestamp", "ouch.timestamp",
1540 FT_STRING, BASE_NONE, NULL, 0x0,
1541 NULL, HFILL }},
1542
1543 { &hf_ouch_trade_correction_reason,
1544 { "Trade Correction Reason", "ouch.trade_correction_reason",
1545 FT_CHAR, BASE_HEX, VALS(ouch_trade_correction_reason_val), 0x0,
1546 NULL, HFILL }}
1547 };
1548
1549 /* Setup protocol subtree array */
1550 static gint *ett[] = {
1551 &ett_ouch
1552 };
1553
1554 /* Register the protocol name and description */
1555 proto_ouch = proto_register_protocol("OUCH", "OUCH", "ouch");
1556
1557 /* Required function calls to register the header fields and
1558 * subtrees used */
1559 proto_register_field_array(proto_ouch, hf, array_length(hf));
1560 proto_register_subtree_array(ett, array_length(ett));
1561 }
1562
1563
1564 /* If this dissector uses sub-dissector registration add a
1565 * registration routine. This format is required because a script is
1566 * used to find these routines and create the code that calls these
1567 * routines. */
1568 void
proto_reg_handoff_ouch(void)1569 proto_reg_handoff_ouch(void)
1570 {
1571 ouch_handle = create_dissector_handle(dissect_ouch, proto_ouch);
1572 heur_dissector_add("soupbintcp", dissect_ouch_heur, "OUCH over SoupBinTCP", "ouch_soupbintcp", proto_ouch, HEURISTIC_ENABLE);
1573 dissector_add_uint_range_with_preference("tcp.port", "", ouch_handle);
1574 }
1575
1576
1577
1578 /*
1579 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1580 *
1581 * Local variables:
1582 * c-basic-offset: 4
1583 * tab-width: 8
1584 * indent-tabs-mode: nil
1585 * End:
1586 *
1587 * vi: set shiftwidth=4 tabstop=8 expandtab:
1588 * :indentSize=4:tabSize=8:noTabs=true:
1589 */
1590