From Didier Gautheron:
[obnox/wireshark/wip.git] / plugins / profinet / packet-pn-ptcp.c
1 /* packet-pn-ptcp.c
2  * Routines for PN-PTCP (PROFINET Precision Time Clock Protocol)
3  * packet dissection.
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #include <string.h>
36
37 #include <glib.h>
38 #include <epan/packet.h>
39 #include <epan/dissectors/packet-dcerpc.h>
40 #include <epan/oui.h>
41
42 #include "packet-pn.h"
43
44
45
46 static int proto_pn_ptcp = -1;
47
48 static int hf_pn_ptcp = -1;
49 static int hf_pn_ptcp_header = -1;
50 static int hf_pn_ptcp_block = -1;
51 static int hf_pn_ptcp_block_tlvheader = -1;
52
53 static int hf_pn_ptcp_res1 = -1;
54 static int hf_pn_ptcp_res2 = -1;
55 static int hf_pn_ptcp_delay10ns = -1;
56 static int hf_pn_ptcp_seq_id = -1;
57 static int hf_pn_ptcp_delay1ns_byte = -1;
58 static int hf_pn_ptcp_delay1ns_fup = -1;
59 static int hf_pn_ptcp_delay1ns = -1;
60
61 static int hf_pn_ptcp_tl_length = -1;
62 static int hf_pn_ptcp_tl_type = -1;
63
64 static int hf_pn_ptcp_master_source_address = -1;
65 static int hf_pn_ptcp_subdomain_uuid = -1;
66
67 static int hf_pn_ptcp_port_mac_address = -1;
68
69 static int hf_pn_ptcp_t2portrxdelay = -1;
70 static int hf_pn_ptcp_t3porttxdelay = -1;
71
72 static int hf_pn_ptcp_t2timestamp = -1;
73
74 static int hf_pn_ptcp_epoch_number = -1;
75 static int hf_pn_ptcp_seconds = -1;
76 static int hf_pn_ptcp_nanoseconds = -1;
77
78 static int hf_pn_ptcp_flags = -1;
79 static int hf_pn_ptcp_currentutcoffset = -1;
80
81 static int hf_pn_ptcp_master_priority1 = -1;
82 static int hf_pn_ptcp_master_priority2 = -1;
83 static int hf_pn_ptcp_clock_class = -1;
84 static int hf_pn_ptcp_clock_accuracy = -1;
85 static int hf_pn_ptcp_clockvariance = -1;
86
87 static int hf_pn_ptcp_oui = -1;
88 static int hf_pn_ptcp_profinet_subtype = -1;
89 static int hf_pn_ptcp_irdata_uuid = -1;
90
91 static gint ett_pn_ptcp = -1;
92 static gint ett_pn_ptcp_header = -1;
93 static gint ett_pn_ptcp_block = -1;
94 static gint ett_pn_ptcp_block_header = -1;
95
96 #define OUI_PROFINET_MULTICAST          0x010ECF        /* PROFIBUS Nutzerorganisation e.V. */
97
98
99 #define PN_PTCP_BT_END              0x00
100 #define PN_PTCP_BT_SUBDOMAIN        0x01
101 #define PN_PTCP_BT_TIME             0x02
102 #define PN_PTCP_BT_TIME_EXTENSION   0x03
103 #define PN_PTCP_BT_MASTER           0x04
104 #define PN_PTCP_BT_PORT_PARAMETER   0x05
105 #define PN_PTCP_BT_DELAY_PARAMETER  0x06
106 #define PN_PTCP_BT_PORT_TIME        0x07
107 #define PN_PTCP_BT_OPTION           0x7F
108 #define PN_PTCP_BT_RTDATA           0x7F
109
110
111 static const value_string pn_ptcp_block_type[] = {
112         { PN_PTCP_BT_END,               "End" },
113         { PN_PTCP_BT_SUBDOMAIN,         "Subdomain"},
114         { PN_PTCP_BT_TIME,              "Time"},
115         { PN_PTCP_BT_TIME_EXTENSION,    "TimeExtension"},
116         { PN_PTCP_BT_MASTER,            "Master"},
117         { PN_PTCP_BT_PORT_PARAMETER,    "PortParameter"},
118         { PN_PTCP_BT_DELAY_PARAMETER,   "DelayParameter"},
119         { PN_PTCP_BT_PORT_TIME,         "PortTime"},
120     /*0x08 - 0x7E Reserved */
121         { PN_PTCP_BT_OPTION,            "Organizationally Specific"},
122     { 0, NULL }
123 };
124
125 static const value_string pn_ptcp_oui_vals[] = {
126         { OUI_PROFINET,             "PROFINET" },
127         { OUI_PROFINET_MULTICAST,   "PROFINET" },
128         { 0, NULL }
129 };
130
131 static const value_string pn_ptcp_master_prio1_vals[] = {
132         { 0x01, "Primary sync. master" },
133         { 0x02, "Secondary sync. master" },
134         { 0, NULL }
135 };
136
137 static const value_string pn_ptcp_master_prio1_short_vals[] = {
138         { 0x01, "Primary" },
139         { 0x02, "Secondary" },
140         { 0, NULL }
141 };
142
143 static const value_string pn_ptcp_master_prio2_vals[] = {
144         { 0xFF, "Default" },
145         { 0, NULL }
146 };
147
148 static const value_string pn_ptcp_clock_class_vals[] = {
149         { 0xFF, "Slave-only clock" },
150         { 0, NULL }
151 };
152
153 static const value_string pn_ptcp_clock_accuracy_vals[] = {
154         { 0x20, "25ns" },
155         { 0x21, "100ns (Default)" },
156         { 0x22, "250ns" },
157         { 0x23, "1us" },
158         { 0x24, "2.5us" },
159         { 0x25, "10us" },
160         { 0x26, "25us" },
161         { 0x27, "100us" },
162         { 0x28, "250us" },
163         { 0x29, "1ms" },
164         { 0xFE, "Unknown" },
165         { 0, NULL }
166 };
167
168 static const value_string pn_ptcp_profinet_subtype_vals[] = {
169         { 0x01, "RTData" },
170         { 0, NULL }
171 };
172
173
174
175
176 static int
177 dissect_PNPTCP_TLVHeader(tvbuff_t *tvb, int offset,
178         packet_info *pinfo, proto_tree *tree, proto_item *item _U_, guint16 *type, guint16 *length)
179 {
180     guint16 tl_type;
181     guint16 tl_length;
182
183
184     /* Type */
185     dissect_pn_uint16(tvb, offset, pinfo, tree, hf_pn_ptcp_tl_type, &tl_type);
186     *type = tl_type >> 9;
187
188     /* Length */
189     offset = dissect_pn_uint16(tvb, offset, pinfo, tree, hf_pn_ptcp_tl_length, &tl_length);
190     *length = tl_length & 0x1FF;
191
192     return offset;
193 }
194
195
196 static int
197 dissect_PNPTCP_Subdomain(tvbuff_t *tvb, int offset,
198         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 u16FrameID)
199 {
200     guint8 mac[6];
201     e_uuid_t uuid;
202
203
204     /* MasterSourceAddress */
205     offset = dissect_pn_mac(tvb, offset, pinfo, tree, hf_pn_ptcp_master_source_address, mac);
206
207     /* SubdomainUUID */
208     offset = dissect_pn_uuid(tvb, offset, pinfo, tree, hf_pn_ptcp_subdomain_uuid, &uuid);
209
210     if(u16FrameID == 0xff00 || u16FrameID == 0xff01) {
211         if (check_col(pinfo->cinfo, COL_INFO))
212           col_append_fstr(pinfo->cinfo, COL_INFO, ", Master=%02x:%02x:%02x:%02x:%02x:%02x",
213             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
214     }
215
216         proto_item_append_text(item, ": MasterSource=%02x:%02x:%02x:%02x:%02x:%02x",
217         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
218
219     proto_item_append_text(item, ", Subdomain=%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
220                                       uuid.Data1, uuid.Data2, uuid.Data3,
221                                       uuid.Data4[0], uuid.Data4[1],
222                                       uuid.Data4[2], uuid.Data4[3],
223                                       uuid.Data4[4], uuid.Data4[5],
224                                       uuid.Data4[6], uuid.Data4[7]);
225
226     return offset;
227 }
228
229
230 static int
231 dissect_PNPTCP_Time(tvbuff_t *tvb, int offset,
232         packet_info *pinfo, proto_tree *tree, proto_item *item)
233 {
234     guint16 EpochNumber;
235     guint32 Seconds;
236     guint32 NanoSeconds;
237
238
239     /* EpochNumber */
240     offset = dissect_pn_uint16(tvb, offset, pinfo, tree, hf_pn_ptcp_epoch_number, &EpochNumber);
241
242     /* Seconds */
243     offset = dissect_pn_uint32(tvb, offset, pinfo, tree, hf_pn_ptcp_seconds, &Seconds);
244
245     /* NanoSeconds */
246     offset = dissect_pn_uint32(tvb, offset, pinfo, tree, hf_pn_ptcp_nanoseconds, &NanoSeconds);
247
248         proto_item_append_text(item, ": Seconds=%u NanoSeconds=%u EpochNumber=%u",
249         Seconds, NanoSeconds, EpochNumber);
250
251     if (check_col(pinfo->cinfo, COL_INFO))
252       col_append_fstr(pinfo->cinfo, COL_INFO, ", Time: %4us %09uns, Epoch: %u", 
253         Seconds, NanoSeconds, EpochNumber);
254
255     return offset;
256 }
257
258
259 static int
260 dissect_PNPTCP_TimeExtension(tvbuff_t *tvb, int offset,
261         packet_info *pinfo, proto_tree *tree, proto_item *item)
262 {
263     guint16 Flags;
264     guint16 CurrentUTCOffset;
265
266
267     /* Flags */
268     offset = dissect_pn_uint16(tvb, offset, pinfo, tree, hf_pn_ptcp_flags, &Flags);
269
270     /* CurrentUTCOffset */
271     offset = dissect_pn_uint16(tvb, offset, pinfo, tree, hf_pn_ptcp_currentutcoffset, &CurrentUTCOffset);
272
273     /* Padding */
274     offset = dissect_pn_align4(tvb, offset, pinfo, tree);
275
276         proto_item_append_text(item, ": Flags=0x%x, CurrentUTCOffset=%u", Flags, CurrentUTCOffset);
277
278     return offset;
279 }
280
281
282 static int
283 dissect_PNPTCP_Master(tvbuff_t *tvb, int offset,
284         packet_info *pinfo, proto_tree *tree, proto_item *item)
285 {
286     guint8 MasterPriority1;
287     guint8 MasterPriority2;
288     guint8 ClockClass;
289     guint8 ClockAccuracy;
290     gint16 ClockVariance;
291
292
293     /* MasterPriority1 */
294     offset = dissect_pn_uint8(tvb, offset, pinfo, tree, hf_pn_ptcp_master_priority1, &MasterPriority1);
295
296     /* MasterPriority2 */
297     offset = dissect_pn_uint8(tvb, offset, pinfo, tree, hf_pn_ptcp_master_priority2, &MasterPriority2);
298
299     /* ClockClass */
300     offset = dissect_pn_uint8(tvb, offset, pinfo, tree, hf_pn_ptcp_clock_class, &ClockClass);
301
302     /* ClockAccuracy */
303     offset = dissect_pn_uint8(tvb, offset, pinfo, tree, hf_pn_ptcp_clock_accuracy, &ClockAccuracy);
304
305     /* ClockVariance */
306     offset = dissect_pn_int16(tvb, offset, pinfo, tree, hf_pn_ptcp_clockvariance, &ClockVariance);
307
308     /* Padding */
309     offset = dissect_pn_align4(tvb, offset, pinfo, tree);
310
311     if (check_col(pinfo->cinfo, COL_INFO))
312       col_append_fstr(pinfo->cinfo, COL_INFO, ", Prio1=\"%s\"",
313         val_to_str(MasterPriority1, pn_ptcp_master_prio1_short_vals, "(Reserved: 0x%x)"));
314
315         proto_item_append_text(item, ": Prio1=\"%s\", Prio2=%s, Clock: Class=\"%s\", Accuracy=%s, Variance=%d",
316         val_to_str(MasterPriority1, pn_ptcp_master_prio1_short_vals, "(Reserved: 0x%x)"), 
317         val_to_str(MasterPriority2, pn_ptcp_master_prio2_vals, "(Reserved: 0x%x)"), 
318         val_to_str(ClockClass, pn_ptcp_clock_class_vals, "(Reserved: 0x%x)"), 
319         val_to_str(ClockAccuracy, pn_ptcp_clock_accuracy_vals, "(Reserved: 0x%x)"), 
320         ClockVariance);
321
322     return offset;
323 }
324
325
326 static int
327 dissect_PNPTCP_PortParameter(tvbuff_t *tvb, int offset,
328         packet_info *pinfo, proto_tree *tree, proto_item *item)
329 {
330     guint32 t2portrxdelay;
331     guint32 t3porttxdelay;
332
333
334     /* Padding */
335     offset = dissect_pn_align4(tvb, offset, pinfo, tree);
336
337     /* T2PortRxDelay */
338     offset = dissect_pn_uint32(tvb, offset, pinfo, tree, hf_pn_ptcp_t2portrxdelay, &t2portrxdelay);
339
340     /* T3PortTxDelay */
341     offset = dissect_pn_uint32(tvb, offset, pinfo, tree, hf_pn_ptcp_t3porttxdelay, &t3porttxdelay);
342
343         proto_item_append_text(item, ": T2PortRxDelay=%uns, T3PortTxDelay=%uns",
344         t2portrxdelay, t3porttxdelay);
345
346     if (check_col(pinfo->cinfo, COL_INFO))
347       col_append_fstr(pinfo->cinfo, COL_INFO, ", T2Rx=%uns, T3Tx=%uns",
348         t2portrxdelay, t3porttxdelay);
349
350     return offset;
351 }
352
353
354 static int
355 dissect_PNPTCP_DelayParameter(tvbuff_t *tvb, int offset,
356         packet_info *pinfo, proto_tree *tree, proto_item *item)
357 {
358     guint8 mac[6];
359
360
361     /* PortMACAddress */
362     offset = dissect_pn_mac(tvb, offset, pinfo, tree, hf_pn_ptcp_port_mac_address, mac);
363
364     /* Padding */
365     offset = dissect_pn_align4(tvb, offset, pinfo, tree);
366
367
368     proto_item_append_text(item, ": PortMAC=%02x:%02x:%02x:%02x:%02x:%02x",
369         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
370
371     if (check_col(pinfo->cinfo, COL_INFO))
372       col_append_fstr(pinfo->cinfo, COL_INFO, ", PortMAC=%02x:%02x:%02x:%02x:%02x:%02x",
373         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
374
375     return offset;
376 }
377
378
379 static int
380 dissect_PNPTCP_PortTime(tvbuff_t *tvb, int offset,
381         packet_info *pinfo, proto_tree *tree, proto_item *item)
382 {
383     guint32 t2timestamp;
384
385
386     /* Padding */
387     offset = dissect_pn_align4(tvb, offset, pinfo, tree);
388
389         /* T2TimeStamp */
390     offset = dissect_pn_uint32(tvb, offset, pinfo, tree, hf_pn_ptcp_t2timestamp, &t2timestamp);
391
392         proto_item_append_text(item, ": T2TimeStamp=%uns", t2timestamp);
393
394     if (check_col(pinfo->cinfo, COL_INFO))
395       col_append_fstr(pinfo->cinfo, COL_INFO, ", T2TS=%uns", t2timestamp);
396
397         return offset;
398 }
399
400
401 static int
402 dissect_PNPTCP_Option_PROFINET(tvbuff_t *tvb, int offset,
403         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 length)
404 {
405     guint8 subType;
406     e_uuid_t uuid;
407
408     /* OUI already dissected! */
409
410     /* SubType */
411     offset = dissect_pn_uint8(tvb, offset, pinfo, tree, hf_pn_ptcp_profinet_subtype, &subType);
412     length --;
413
414     switch(subType) {
415     case 1: /* RTData */
416         /* Padding */
417         offset = dissect_pn_align4(tvb, offset, pinfo, tree);
418
419         /* IRDataUUID */
420         offset = dissect_pn_uuid(tvb, offset, pinfo, tree, hf_pn_ptcp_irdata_uuid, &uuid);
421         proto_item_append_text(item, ": IRDataUUID=%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
422                                       uuid.Data1, uuid.Data2, uuid.Data3,
423                                       uuid.Data4[0], uuid.Data4[1],
424                                       uuid.Data4[2], uuid.Data4[3],
425                                       uuid.Data4[4], uuid.Data4[5],
426                                       uuid.Data4[6], uuid.Data4[7]);
427
428         break;
429     default:
430         offset = dissect_pn_undecoded(tvb, offset, pinfo, tree, length);
431         break;
432     }
433
434     return offset;
435 }
436
437
438 static int
439 dissect_PNPTCP_Option(tvbuff_t *tvb, int offset,
440         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 length)
441 {
442         guint32 oui;
443
444
445     /* verify remaining TLV length */
446         if (length < 4)
447         {
448         if (tree) {
449             /* too short */
450             offset = dissect_pn_undecoded(tvb, offset, pinfo, tree, length);
451         }
452                 return (offset);
453         }
454
455         /* OUI (organizational unique id) */
456     offset = dissect_pn_oid(tvb, offset, pinfo,tree, hf_pn_ptcp_oui, &oui);
457     length -= 3;
458
459         switch (oui)
460         {
461         case OUI_PROFINET:
462         case OUI_PROFINET_MULTICAST:
463         proto_item_append_text(item, ": PROFINET");
464         offset = dissect_PNPTCP_Option_PROFINET(tvb, offset, pinfo, tree, item, length);
465                 break;
466         default:
467         /* SubType */
468         offset = dissect_pn_undecoded(tvb, offset, pinfo, tree, length);
469         }
470
471         return (offset);
472 }
473
474
475 static int
476 dissect_PNPTCP_block(tvbuff_t *tvb, int offset,
477         packet_info *pinfo, proto_tree *tree, proto_item *item _U_, gboolean *end, guint16 u16FrameID)
478 {
479     guint16 type;
480     guint16 length;
481
482         proto_item *sub_item;
483         proto_tree *sub_tree;
484         proto_item *tlvheader_item;
485         proto_tree *tlvheader_tree;
486         guint32 u32SubStart;
487
488
489     *end = FALSE;
490
491     /* block subtree */
492     sub_item = proto_tree_add_item(tree, hf_pn_ptcp_block, tvb, offset, 0, FALSE);
493         sub_tree = proto_item_add_subtree(sub_item, ett_pn_ptcp_block);
494     u32SubStart = offset;
495
496     /* tlvheader subtree */
497     tlvheader_item = proto_tree_add_item(sub_tree, hf_pn_ptcp_block_tlvheader, tvb, offset, 2 /* len */, FALSE);
498         tlvheader_tree = proto_item_add_subtree(tlvheader_item, ett_pn_ptcp_block_header);
499
500     offset = dissect_PNPTCP_TLVHeader(tvb, offset, pinfo, tlvheader_tree, sub_item, &type, &length);
501
502         proto_item_set_text(sub_item, "%s",
503         val_to_str(type, pn_ptcp_block_type, "Unknown"));
504
505         proto_item_append_text(tlvheader_item, ": Type=%s (%x), Length=%u",
506         val_to_str(type, pn_ptcp_block_type, "Unknown"), type, length);
507
508     switch(type) {
509     case(0x00): /* End, no content */
510         *end = TRUE;
511         break;
512     case(0x01): /* Subdomain */
513         dissect_PNPTCP_Subdomain(tvb, offset, pinfo, sub_tree, sub_item, u16FrameID);
514         break;
515     case(0x02): /* Time */
516         dissect_PNPTCP_Time(tvb, offset, pinfo, sub_tree, sub_item);
517         break;
518     case(0x03): /* TimeExtension */
519         dissect_PNPTCP_TimeExtension(tvb, offset, pinfo, sub_tree, sub_item);
520         break;
521     case(0x04): /* Master */
522         dissect_PNPTCP_Master(tvb, offset, pinfo, sub_tree, sub_item);
523         break;
524     case(0x05): /* PortParameter */
525         dissect_PNPTCP_PortParameter(tvb, offset, pinfo, sub_tree, sub_item);
526         break;
527     case(0x06): /* DelayParameter */
528         dissect_PNPTCP_DelayParameter(tvb, offset, pinfo, sub_tree, sub_item);
529         break;
530     case(0x07): /* PortTime */
531         dissect_PNPTCP_PortTime(tvb, offset, pinfo, sub_tree, sub_item);
532         break;
533     case(0x7F): /* Organizational Specific */
534         dissect_PNPTCP_Option(tvb, offset, pinfo, sub_tree, sub_item, length);
535         break;
536     default:
537         offset = dissect_pn_undecoded(tvb, offset, pinfo, tree, length);
538     }
539     offset += length;
540
541         proto_item_set_len(sub_item, offset - u32SubStart);
542
543     return offset;
544 }
545
546
547 static int
548 dissect_PNPTCP_blocks(tvbuff_t *tvb, int offset,
549         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 u16FrameID)
550 {
551     gboolean end = FALSE;
552
553     /* as long as we have some bytes, try a new block */
554     while(!end) {
555         offset = dissect_PNPTCP_block(tvb, offset, pinfo, tree, item, &end, u16FrameID);
556     }
557
558     return offset;
559 }
560
561
562 static int
563 dissect_PNPTCP_FollowUpPDU(tvbuff_t *tvb, int offset,
564         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 u16FrameID, const char *name, const char *name_short)
565 {
566         proto_item *header_item;
567         proto_tree *header_tree;
568     guint16 seq_id;
569     gint32 delay1ns_fup;
570
571
572     /* dissect the header */
573     header_item = proto_tree_add_item(tree, hf_pn_ptcp_header, tvb, offset, 20 /* len */, FALSE);
574         header_tree = proto_item_add_subtree(header_item, ett_pn_ptcp_header);
575
576     /* Padding 12 bytes */
577     offset = dissect_pn_padding(tvb, offset, pinfo, header_tree, 12);
578
579     /* SequenceID */
580     offset = dissect_pn_uint16(tvb, offset, pinfo, header_tree, hf_pn_ptcp_seq_id, &seq_id);
581
582     /* Padding */
583     offset = dissect_pn_align4(tvb, offset, pinfo, header_tree);
584
585     /* Delay1ns_FUP */
586     offset = dissect_pn_int32(tvb, offset, pinfo, header_tree, hf_pn_ptcp_delay1ns_fup, &delay1ns_fup);
587
588     if (check_col(pinfo->cinfo, COL_INFO))
589       col_append_fstr(pinfo->cinfo, COL_INFO, "%s, Seq=%3u, Delay=%11dns", name, seq_id, delay1ns_fup);
590     proto_item_append_text(item, "%s: Sequence=%u, Delay=%dns", name_short, seq_id, delay1ns_fup);
591     proto_item_append_text(header_item, ": Sequence=%u, Delay=%dns", seq_id, delay1ns_fup);
592
593
594     /* dissect the TLV blocks */
595     offset = dissect_PNPTCP_blocks(tvb, offset, pinfo, tree, item, u16FrameID);
596
597     return offset;
598 }
599
600
601 static int
602 dissect_PNPTCP_RTSyncPDU(tvbuff_t *tvb, int offset,
603         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 u16FrameID, const char *name, const char *name_short)
604 {
605         proto_item *header_item;
606         proto_tree *header_tree;
607     guint32 res_1;
608     guint32 res_2;
609     guint32 delay10ns;
610     guint16 seq_id;
611     guint8 delay1ns_8;
612     guint64 delay1ns_64;
613     guint32 delay1ns_32;
614     guint32 delayms;
615
616
617     header_item = proto_tree_add_item(tree, hf_pn_ptcp_header, tvb, offset, 20 /* len */, FALSE);
618         header_tree = proto_item_add_subtree(header_item, ett_pn_ptcp_header);
619
620     /* Reserved_1 */
621     offset = dissect_pn_uint32(tvb, offset, pinfo, header_tree, hf_pn_ptcp_res1, &res_1);
622
623     /* Reserved_2 */
624     offset = dissect_pn_uint32(tvb, offset, pinfo, header_tree, hf_pn_ptcp_res2, &res_2);
625
626     /* Delay10ns */
627     offset = dissect_pn_uint32(tvb, offset, pinfo, header_tree, hf_pn_ptcp_delay10ns, &delay10ns);
628
629     /* SequenceID */
630     offset = dissect_pn_uint16(tvb, offset, pinfo, header_tree, hf_pn_ptcp_seq_id, &seq_id);
631
632     /* Delay1ns */
633     offset = dissect_pn_uint8(tvb, offset, pinfo, header_tree, hf_pn_ptcp_delay1ns_byte, &delay1ns_8);
634
635     /* Padding */
636     offset = dissect_pn_align4(tvb, offset, pinfo, header_tree);
637
638     /* Delay1ns */
639     offset = dissect_pn_uint32(tvb, offset, pinfo, header_tree, hf_pn_ptcp_delay1ns, &delay1ns_32);
640
641     /* Padding */
642     offset = dissect_pn_align4(tvb, offset, pinfo, tree);
643
644
645     delay1ns_64 = ((guint64) delay10ns) * 10 + delay1ns_8 + delay1ns_32;
646     delayms = (guint32) (delay1ns_64 / (1000 * 1000));
647
648     if (check_col(pinfo->cinfo, COL_INFO))
649         col_append_fstr(pinfo->cinfo, COL_INFO, "%s, Seq=%3u, Delay=%11" G_GINT64_MODIFIER "uns",
650             name, seq_id, delay1ns_64);
651     proto_item_append_text(item, "%s: Sequence=%u, Delay=%" G_GINT64_MODIFIER "uns", 
652         name_short, seq_id, delay1ns_64);
653     proto_item_append_text(header_item, ": Sequence=%u, Delay=%" G_GINT64_MODIFIER "uns", 
654         seq_id, delay1ns_64);
655
656     if(delay1ns_64 != 0)
657         proto_item_append_text(header_item, " (%u.%03u,%03u,%03u sec)",
658             delayms / 1000,
659             delayms % 1000,
660             (delay10ns % (1000*100)) / 100,
661              delay10ns % 100 * 10 + delay1ns_8);
662
663     /* dissect the PDU */
664     offset = dissect_PNPTCP_blocks(tvb, offset, pinfo, tree, item, u16FrameID);
665
666     return offset;
667 }
668
669
670 static int
671 dissect_PNPTCP_AnnouncePDU(tvbuff_t *tvb, int offset,
672         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 u16FrameID, const char *name, const char *name_short)
673 {
674         proto_item *header_item;
675         proto_tree *header_tree;
676     guint16 seq_id;
677
678
679     /* dissect the header */
680     header_item = proto_tree_add_item(tree, hf_pn_ptcp_header, tvb, offset, 20 /* len */, FALSE);
681         header_tree = proto_item_add_subtree(header_item, ett_pn_ptcp_header);
682
683     /* Padding 12 bytes */
684     offset = dissect_pn_padding(tvb, offset, pinfo, header_tree, 12);
685
686     /* SequenceID */
687     offset = dissect_pn_uint16(tvb, offset, pinfo, header_tree, hf_pn_ptcp_seq_id, &seq_id);
688
689     /* Padding 6 bytes */
690     offset = dissect_pn_padding(tvb, offset, pinfo, header_tree, 6);
691
692     if (check_col(pinfo->cinfo, COL_INFO))
693       col_append_fstr(pinfo->cinfo, COL_INFO, "%s, Seq=%3u", name, seq_id);
694     proto_item_append_text(item, "%s: Sequence=%u", name_short, seq_id);
695     proto_item_append_text(header_item, ": Sequence=%u", seq_id);
696
697
698     /* dissect the PDU */
699     offset = dissect_PNPTCP_blocks(tvb, offset, pinfo, tree, item, u16FrameID);
700
701     return offset;
702 }
703
704
705 static int
706 dissect_PNPTCP_DelayPDU(tvbuff_t *tvb, int offset,
707         packet_info *pinfo, proto_tree *tree, proto_item *item, guint16 u16FrameID, const char *name, const char *name_short)
708 {
709         proto_item *header_item;
710         proto_tree *header_tree;
711     guint16 seq_id;
712     guint32 delay1ns;
713
714
715     /* dissect the header */
716     header_item = proto_tree_add_item(tree, hf_pn_ptcp_header, tvb, offset, 20 /* len */, FALSE);
717         header_tree = proto_item_add_subtree(header_item, ett_pn_ptcp_header);
718
719     /* Padding 12 bytes */
720     offset = dissect_pn_padding(tvb, offset, pinfo, header_tree, 12);
721
722     /* SequenceID */
723     offset = dissect_pn_uint16(tvb, offset, pinfo, header_tree, hf_pn_ptcp_seq_id, &seq_id);
724
725     /* Padding */
726     offset = dissect_pn_align4(tvb, offset, pinfo, header_tree);
727
728     /* Delay1ns_FUP */
729     offset = dissect_pn_uint32(tvb, offset, pinfo, header_tree, hf_pn_ptcp_delay1ns, &delay1ns);
730
731     if (check_col(pinfo->cinfo, COL_INFO))
732       col_append_fstr(pinfo->cinfo, COL_INFO, "%s, Seq=%3u, Delay=%11uns", name, seq_id, delay1ns);
733     proto_item_append_text(item, "%s: Sequence=%u, Delay=%uns", name_short, seq_id, delay1ns);
734     proto_item_append_text(header_item, ": Sequence=%u, Delay=%uns", seq_id, delay1ns);
735
736
737     /* dissect the PDU */
738     offset = dissect_PNPTCP_blocks(tvb, offset, pinfo, tree, item, u16FrameID);
739
740     return offset;
741 }
742
743
744 /* possibly dissect a PN-RT packet (frame ID must be in the appropriate range) */
745 static gboolean
746 dissect_PNPTCP_Data_heur(tvbuff_t *tvb,
747         packet_info *pinfo, proto_tree *tree)
748 {
749     guint16 u16FrameID;
750     proto_item *item = NULL;
751     proto_tree *ptcp_tree = NULL;
752     int offset = 0;
753         guint32 u32SubStart;
754     /*proto_item *unknown_item = NULL;*/
755
756
757     /* the tvb will NOT contain the frame_id here, so get it from our private data! */
758     u16FrameID = GPOINTER_TO_UINT(pinfo->private_data);
759
760         /* frame id must be in valid range (acyclic Real-Time, PTCP) */
761     /* 0x0000 - 0x007F: RTSyncPDU (with follow up) */
762     /* 0x0080 - 0x00FF: RTSyncPDU (without follow up) */
763     /* 0xFF00 - 0xFF1F: AnnouncePDU */
764     /* 0xFF20 - 0xFF3F: FollowUpPDU */
765     /* 0xFF40 - 0xFF5F: Delay...PDU */
766         if ( (u16FrameID >= 0x0100 && u16FrameID < 0xFF00) || (u16FrameID > 0xFF5F) ) {
767         /* we are not interested in this packet */
768         return FALSE;
769     }
770
771     col_set_str(pinfo->cinfo, COL_PROTOCOL, "PN-PTCP");
772     col_clear(pinfo->cinfo, COL_INFO);
773
774     /* subtree for PTCP */
775         item = proto_tree_add_protocol_format(tree, proto_pn_ptcp, tvb, 0, 0, "PROFINET PTCP, ");
776         ptcp_tree = proto_item_add_subtree(item, ett_pn_ptcp);
777     u32SubStart = offset;
778
779     switch(u16FrameID) {
780     /* range 1 (0x0000 - 0x007F) */
781         /* 0x0000 - 0x001F reserved */
782     case(0x0020):
783         offset = dissect_PNPTCP_RTSyncPDU   (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "RTSync FU (Clock)", "RTSync FU (Clock)");
784         break;
785     case(0x0021):
786         offset = dissect_PNPTCP_RTSyncPDU   (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "RTSync FU  (Time)", "RTSync FU (Time)");
787         break;
788         /* 0x0022 - 0x007F reserved */
789
790     /* range 2 (0x0080 - 0x00FF) */
791     case(0x0080):
792         offset = dissect_PNPTCP_RTSyncPDU   (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "RTSync    (Clock)", "RTSync (Clock)");
793         break;
794     case(0x0081):
795         offset = dissect_PNPTCP_RTSyncPDU   (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "RTSync     (Time)", "RTSync (Time)");
796         break;
797         /* 0x0081 - 0x00FF reserved */
798
799     /* range 7 (0xFF00 - 0xFF5F) */
800     case(0xff00):
801         offset = dissect_PNPTCP_AnnouncePDU (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "Announce  (Clock)", "Announce (Clock)");
802         break;
803     case(0xff01):
804         offset = dissect_PNPTCP_AnnouncePDU (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "Announce   (Time)", "Announce (Time)");
805         break;
806         /* 0xFF02 - 0xFF1F reserved */
807     case(0xff20):
808         offset = dissect_PNPTCP_FollowUpPDU (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "FollowUp  (Clock)", "FollowUp (Clock)");
809         break;
810     case(0xff21):
811         offset = dissect_PNPTCP_FollowUpPDU (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "FollowUp   (Time)", "FollowUp (Time)");
812         break;
813         /* 0xFF22 - 0xFF3F reserved */
814     case(0xff40):
815         offset = dissect_PNPTCP_DelayPDU    (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "DelayReq         ", "DelayReq");
816         break;
817     case(0xff41):
818         offset = dissect_PNPTCP_DelayPDU    (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "DelayRes         ", "DelayRes");
819         break;
820     case(0xff42):
821         offset = dissect_PNPTCP_DelayPDU    (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "DelayFuRes       ", "DelayFuRes");
822         break;
823     case(0xff43):
824         offset = dissect_PNPTCP_DelayPDU    (tvb, offset, pinfo, ptcp_tree, item, u16FrameID, "DelayRes         ", "DelayRes");
825         break;
826         /* 0xFF44 - 0xFF5F reserved */
827     default:
828         offset = dissect_pn_undecoded(tvb, offset, pinfo, tree, tvb_length_remaining(tvb, offset));
829
830         if (check_col(pinfo->cinfo, COL_INFO))
831                 col_append_fstr(pinfo->cinfo, COL_INFO, "Reserved FrameID 0x%04x", u16FrameID);
832
833         proto_item_append_text(item, "Reserved FrameID 0x%04x", u16FrameID);
834
835         offset += tvb_length_remaining(tvb, offset);
836     }
837
838         proto_item_set_len(item, offset - u32SubStart);
839
840     return TRUE;
841 }
842
843
844 void
845 proto_register_pn_ptcp (void)
846 {
847         static hf_register_info hf[] = {
848         { &hf_pn_ptcp,
849                 { "PROFINET PTCP", "pn_ptcp", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
850         { &hf_pn_ptcp_header,
851         { "Header", "pn_ptcp.header", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
852         { &hf_pn_ptcp_block,
853         { "Block", "pn_ptcp.block", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
854         { &hf_pn_ptcp_block_tlvheader,
855         { "TLVHeader", "pn_ptcp.tlvheader", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
856
857         { &hf_pn_ptcp_res1,
858                 { "Reserved 1", "pn_ptcp.res1", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
859         { &hf_pn_ptcp_res2,
860                 { "Reserved 2", "pn_ptcp.res2", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
861         { &hf_pn_ptcp_delay10ns,
862                 { "Delay10ns", "pn_ptcp.delay10ns", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
863         { &hf_pn_ptcp_seq_id,
864                 { "SequenceID", "pn_ptcp.sequence_id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
865         { &hf_pn_ptcp_delay1ns_byte,
866                 { "Delay1ns_Byte", "pn_ptcp.delay1ns_byte", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
867         { &hf_pn_ptcp_delay1ns,
868                 { "Delay1ns", "pn_ptcp.delay1ns", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
869         { &hf_pn_ptcp_delay1ns_fup,
870                 { "Delay1ns_FUP", "pn_ptcp.delay1ns_fup", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
871
872         { &hf_pn_ptcp_tl_length,
873         { "TypeLength.Length", "pn_ptcp.tl_length", FT_UINT16, BASE_DEC, 0x0, 0x1FF, NULL, HFILL }},
874         { &hf_pn_ptcp_tl_type,
875         { "TypeLength.Type", "pn_ptcp.tl_type", FT_UINT16, BASE_DEC, 0x0, 0xFE00, NULL, HFILL }},
876
877         { &hf_pn_ptcp_master_source_address,
878         { "MasterSourceAddress", "pn_ptcp.master_source_address", FT_ETHER, BASE_NONE, 0x0, 0x0, NULL, HFILL }},
879         { &hf_pn_ptcp_subdomain_uuid,
880         { "SubdomainUUID", "pn_ptcp.subdomain_uuid", FT_GUID, BASE_NONE, 0x0, 0x0, NULL, HFILL }},
881
882     { &hf_pn_ptcp_port_mac_address,
883         { "PortMACAddress", "pn_ptcp.port_mac_address", FT_ETHER, BASE_NONE, 0x0, 0x0, NULL, HFILL }},
884
885         { &hf_pn_ptcp_t2portrxdelay,
886         { "T2PortRxDelay (ns)", "pn_ptcp.t2portrxdelay", FT_UINT32, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
887         { &hf_pn_ptcp_t3porttxdelay,
888         { "T3PortTxDelay (ns)", "pn_ptcp.t3porttxdelay", FT_UINT32, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
889         { &hf_pn_ptcp_t2timestamp,
890         { "T2TimeStamp (ns)", "pn_ptcp.t2timestamp", FT_UINT32, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
891
892         { &hf_pn_ptcp_epoch_number,
893         { "EpochNumber", "pn_ptcp.epoch_number", FT_UINT16, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
894         { &hf_pn_ptcp_seconds,
895         { "Seconds", "pn_ptcp.seconds", FT_UINT32, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
896         { &hf_pn_ptcp_nanoseconds,
897         { "NanoSeconds", "pn_ptcp.nanoseconds", FT_UINT32, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
898
899         { &hf_pn_ptcp_flags,
900         { "Flags", "pn_ptcp.flags", FT_UINT16, BASE_HEX, 0x0, 0x0, NULL, HFILL }},
901         { &hf_pn_ptcp_currentutcoffset,
902         { "CurrentUTCOffset", "pn_ptcp.currentutcoffset", FT_UINT16, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
903
904         { &hf_pn_ptcp_master_priority1,
905         { "MasterPriority1", "pn_ptcp.master_priority1", FT_UINT8, BASE_DEC, VALS(pn_ptcp_master_prio1_vals), 0x0, NULL, HFILL }},
906         { &hf_pn_ptcp_master_priority2,
907         { "MasterPriority2", "pn_ptcp.master_priority2", FT_UINT8, BASE_DEC, VALS(pn_ptcp_master_prio2_vals), 0x0, NULL, HFILL }},
908         { &hf_pn_ptcp_clock_class,
909         { "ClockClass", "pn_ptcp.clock_class", FT_UINT8, BASE_DEC, VALS(pn_ptcp_clock_class_vals), 0x0, NULL, HFILL }},
910         { &hf_pn_ptcp_clock_accuracy,
911         { "ClockAccuracy", "pn_ptcp.clock_accuracy", FT_UINT8, BASE_DEC, VALS(pn_ptcp_clock_accuracy_vals), 0x0, NULL, HFILL }},
912         { &hf_pn_ptcp_clockvariance,
913         { "ClockVariance", "pn_ptcp.clockvariance", FT_INT16, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
914
915         { &hf_pn_ptcp_oui,
916                 { "Organizationally Unique Identifier", "pn_ptcp.oui", FT_UINT24, BASE_HEX,
917                 VALS(pn_ptcp_oui_vals), 0x0, NULL, HFILL }},
918         { &hf_pn_ptcp_profinet_subtype,
919                 { "Subtype",    "pn_ptcp.subtype", FT_UINT8, BASE_HEX,
920                 VALS(pn_ptcp_profinet_subtype_vals), 0x0, "PROFINET Subtype", HFILL }},
921
922         { &hf_pn_ptcp_irdata_uuid,
923         { "IRDataUUID", "pn_ptcp.irdata_uuid", FT_GUID, BASE_NONE, 0x0, 0x0, NULL, HFILL }},
924         };
925
926         static gint *ett[] = {
927                 &ett_pn_ptcp,
928                 &ett_pn_ptcp_header,
929         &ett_pn_ptcp_block,
930         &ett_pn_ptcp_block_header
931     };
932         proto_pn_ptcp = proto_register_protocol ("PROFINET PTCP", "PN-PTCP", "pn_ptcp");
933         proto_register_field_array (proto_pn_ptcp, hf, array_length (hf));
934         proto_register_subtree_array (ett, array_length (ett));
935 }
936
937 void
938 proto_reg_handoff_pn_ptcp (void)
939 {
940     /* register ourself as an heuristic pn-rt payload dissector */
941         heur_dissector_add("pn_rt", dissect_PNPTCP_Data_heur, proto_pn_ptcp);
942 }