Trivial warning fixes
[obnox/wireshark/wip.git] / epan / dissectors / packet-pgm.c
1 /* packet-pgm.c
2  * Routines for PGM packet disassembly, RFC 3208
3  *
4  * $Id$
5  *
6  * Copyright (c) 2000 by Talarian Corp
7  * Rewritten by Jaap Keuter
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1999 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <time.h>
36 #include <string.h>
37 #include <epan/packet.h>
38 #include <epan/afn.h>
39 #include <epan/ipproto.h>
40 #include <epan/in_cksum.h>
41 #include <epan/addr_resolv.h>
42 #include <epan/strutil.h>
43 #include <epan/prefs.h>
44 #include <epan/proto.h>
45 #include <epan/emem.h>
46 #include <epan/ptvcursor.h>
47
48 /*
49  * Flag to control whether to check the PGM checksum.
50  */
51 static gboolean pgm_check_checksum = TRUE;
52
53 void proto_reg_handoff_pgm(void);
54
55 /* constants for hdr types */
56 #define PGM_SPM_PCKT  0x00
57 #define PGM_ODATA_PCKT  0x04
58 #define PGM_RDATA_PCKT  0x05
59 #define PGM_NAK_PCKT  0x08
60 #define PGM_NNAK_PCKT  0x09
61 #define PGM_NCF_PCKT 0x0A
62 #define PGM_POLL_PCKT 0x01
63 #define PGM_POLR_PCKT 0x02
64 #define PGM_ACK_PCKT 0x0D
65
66 /* option flags (main PGM header) */
67 #define PGM_OPT 0x01
68 #define PGM_OPT_NETSIG 0x02
69 #define PGM_OPT_VAR_PKTLEN 0x40
70 #define PGM_OPT_PARITY 0x80
71
72 /* option types */
73 #define PGM_OPT_LENGTH 0x00
74 #define PGM_OPT_END 0x80
75 #define PGM_OPT_FRAGMENT 0x01
76 #define PGM_OPT_NAK_LIST 0x02
77 #define PGM_OPT_JOIN 0x03
78 #define PGM_OPT_REDIRECT 0x07
79 #define PGM_OPT_SYN 0x0D
80 #define PGM_OPT_FIN 0x0E
81 #define PGM_OPT_RST 0x0F
82 #define PGM_OPT_PARITY_PRM 0x08
83 #define PGM_OPT_PARITY_GRP 0x09
84 #define PGM_OPT_CURR_TGSIZE 0x0A
85 #define PGM_OPT_PGMCC_DATA  0x12
86 #define PGM_OPT_PGMCC_FEEDBACK  0x13
87 #define PGM_OPT_NAK_BO_IVL 0x04
88 #define PGM_OPT_NAK_BO_RNG 0x05
89
90 /* POLL subtypes */
91 #define PGM_POLL_GENERAL 0x0
92 #define PGM_POLL_DLR 0x1
93
94 /* OPX bit values */
95 #define PGM_OPX_IGNORE  0x00
96 #define PGM_OPX_INVAL   0x01
97 #define PGM_OPX_DISCARD 0x10
98
99 #define PGM_OPT_NAK_LIST_SIZE 4
100
101 /*
102  * To squeeze the whole option into 255 bytes, we
103  * can only have 62 in the list
104  */
105 #define PGM_MAX_NAK_LIST_SZ (62)
106
107 #define PGM_OPT_JOIN_SIZE 8
108 #define PGM_OPT_PARITY_PRM_SIZE 8
109
110 /* OPT_PARITY_PRM P and O bits */
111 #define PGM_OPT_PARITY_PRM_PRO 0x2
112 #define PGM_OPT_PARITY_PRM_OND 0x1
113
114 #define PGM_OPT_PARITY_GRP_SIZE 8
115 #define PGM_OPT_CURR_TGSIZE_SIZE 8
116 #define PGM_OPT_PGMCC_DATA_SIZE 16
117 #define PGM_OPT_PGMCC_FEEDBACK_SIZE 16
118 #define PGM_OPT_NAK_BO_IVL_SIZE 12
119 #define PGM_OPT_NAK_BO_RNG_SIZE 12
120 #define PGM_OPT_REDIRECT_SIZE 12
121 #define PGM_OPT_FRAGMENT_SIZE 16
122
123 /*
124  * Udp port for UDP encapsulation
125  */
126 #define DEFAULT_UDP_ENCAP_UCAST_PORT 3055
127 #define DEFAULT_UDP_ENCAP_MCAST_PORT 3056
128
129 static guint udp_encap_ucast_port = 0;
130 static guint udp_encap_mcast_port = 0;
131
132 static int proto_pgm = -1;
133 static int ett_pgm = -1;
134 static int ett_pgm_optbits = -1;
135 static int ett_pgm_opts = -1;
136 static int ett_pgm_spm = -1;
137 static int ett_pgm_data = -1;
138 static int ett_pgm_nak = -1;
139 static int ett_pgm_poll = -1;
140 static int ett_pgm_polr = -1;
141 static int ett_pgm_ack = -1;
142 static int ett_pgm_opts_join = -1;
143 static int ett_pgm_opts_parityprm = -1;
144 static int ett_pgm_opts_paritygrp = -1;
145 static int ett_pgm_opts_naklist = -1;
146 static int ett_pgm_opts_ccdata = -1;
147 static int ett_pgm_opts_nak_bo_ivl = -1;
148 static int ett_pgm_opts_nak_bo_rng = -1;
149 static int ett_pgm_opts_redirect = -1;
150 static int ett_pgm_opts_fragment = -1;
151
152 static int hf_pgm_main_sport = -1;
153 static int hf_pgm_main_dport = -1;
154 static int hf_pgm_port = -1;
155 static int hf_pgm_main_type = -1;
156 static int hf_pgm_main_opts = -1;
157 static int hf_pgm_main_opts_opt = -1;
158 static int hf_pgm_main_opts_netsig = -1;
159 static int hf_pgm_main_opts_varlen = -1;
160 static int hf_pgm_main_opts_parity = -1;
161 static int hf_pgm_main_cksum = -1;
162 static int hf_pgm_main_cksum_bad = -1;
163 static int hf_pgm_main_gsi = -1;
164 static int hf_pgm_main_tsdulen = -1;
165 static int hf_pgm_spm_sqn = -1;
166 static int hf_pgm_spm_lead = -1;
167 static int hf_pgm_spm_trail = -1;
168 static int hf_pgm_spm_pathafi = -1;
169 static int hf_pgm_spm_res = -1;
170 static int hf_pgm_spm_path = -1;
171 static int hf_pgm_spm_path6 = -1;
172 static int hf_pgm_data_sqn = -1;
173 static int hf_pgm_data_trail = -1;
174 static int hf_pgm_nak_sqn = -1;
175 static int hf_pgm_nak_srcafi = -1;
176 static int hf_pgm_nak_srcres = -1;
177 static int hf_pgm_nak_src = -1;
178 static int hf_pgm_nak_src6 = -1;
179 static int hf_pgm_nak_grpafi = -1;
180 static int hf_pgm_nak_grpres = -1;
181 static int hf_pgm_nak_grp = -1;
182 static int hf_pgm_nak_grp6 = -1;
183 static int hf_pgm_poll_sqn = -1;
184 static int hf_pgm_poll_round = -1;
185 static int hf_pgm_poll_subtype = -1;
186 static int hf_pgm_poll_pathafi = -1;
187 static int hf_pgm_poll_res = -1;
188 static int hf_pgm_poll_path = -1;
189 static int hf_pgm_poll_path6 = -1;
190 static int hf_pgm_poll_backoff_ivl = -1;
191 static int hf_pgm_poll_rand_str = -1;
192 static int hf_pgm_poll_matching_bmask = -1;
193 static int hf_pgm_polr_sqn = -1;
194 static int hf_pgm_polr_round = -1;
195 static int hf_pgm_polr_res = -1;
196 static int hf_pgm_ack_sqn = -1;
197 static int hf_pgm_ack_bitmap = -1;
198
199 static int hf_pgm_opt_type = -1;
200 static int hf_pgm_opt_len = -1;
201 static int hf_pgm_opt_tlen = -1;
202
203 static int hf_pgm_genopt_type = -1;
204 static int hf_pgm_genopt_len = -1;
205 static int hf_pgm_genopt_opx = -1;
206
207 static int hf_pgm_opt_join_res = -1;
208 static int hf_pgm_opt_join_minjoin = -1;
209
210 static int hf_pgm_opt_parity_prm_po = -1;
211 static int hf_pgm_opt_parity_prm_prmtgsz = -1;
212
213 static int hf_pgm_opt_parity_grp_res = -1;
214 static int hf_pgm_opt_parity_grp_prmgrp = -1;
215
216 static int hf_pgm_opt_nak_res = -1;
217 static int hf_pgm_opt_nak_list = -1;
218
219 static int hf_pgm_opt_ccdata_res = -1;
220 static int hf_pgm_opt_ccdata_tsp = -1;
221 static int hf_pgm_opt_ccdata_afi = -1;
222 static int hf_pgm_opt_ccdata_res2 = -1;
223 static int hf_pgm_opt_ccdata_acker = -1;
224 static int hf_pgm_opt_ccdata_acker6 = -1;
225
226 static int hf_pgm_opt_ccfeedbk_res = -1;
227 static int hf_pgm_opt_ccfeedbk_tsp = -1;
228 static int hf_pgm_opt_ccfeedbk_afi = -1;
229 static int hf_pgm_opt_ccfeedbk_lossrate = -1;
230 static int hf_pgm_opt_ccfeedbk_acker = -1;
231 static int hf_pgm_opt_ccfeedbk_acker6 = -1;
232
233 static int hf_pgm_opt_nak_bo_ivl_res = -1;
234 static int hf_pgm_opt_nak_bo_ivl_bo_ivl = -1;
235 static int hf_pgm_opt_nak_bo_ivl_bo_ivl_sqn = -1;
236
237 static int hf_pgm_opt_nak_bo_rng_res = -1;
238 static int hf_pgm_opt_nak_bo_rng_min_bo_ivl = -1;
239 static int hf_pgm_opt_nak_bo_rng_max_bo_ivl = -1;
240
241 static int hf_pgm_opt_redirect_res = -1;
242 static int hf_pgm_opt_redirect_afi = -1;
243 static int hf_pgm_opt_redirect_res2 = -1;
244 static int hf_pgm_opt_redirect_dlr = -1;
245 static int hf_pgm_opt_redirect_dlr6 = -1;
246
247 static int hf_pgm_opt_fragment_res = -1;
248 static int hf_pgm_opt_fragment_first_sqn = -1;
249 static int hf_pgm_opt_fragment_offset = -1;
250 static int hf_pgm_opt_fragment_total_length = -1;
251
252 static dissector_table_t subdissector_table;
253 static heur_dissector_list_t heur_subdissector_list;
254 static dissector_handle_t data_handle;
255
256
257 static const char *
258 optsstr(guint8 opts)
259 {
260         char *msg;
261         size_t returned_length, index = 0;
262         const int MAX_STR_LEN = 256;
263
264         if (opts == 0)
265                 return("");
266
267         msg=ep_alloc(MAX_STR_LEN);
268         if (opts & PGM_OPT){
269                 returned_length = g_snprintf(&msg[index], MAX_STR_LEN-index, "Present");
270                 index += MIN(returned_length, MAX_STR_LEN-index);
271         }
272         if (opts & PGM_OPT_NETSIG){
273                 returned_length = g_snprintf(&msg[index], MAX_STR_LEN-index, "%sNetSig", (!index)?"":",");
274                 index += MIN(returned_length, MAX_STR_LEN-index);
275         }
276         if (opts & PGM_OPT_VAR_PKTLEN){
277                 returned_length = g_snprintf(&msg[index], MAX_STR_LEN-index, "%sVarLen", (!index)?"":",");
278                 index += MIN(returned_length, MAX_STR_LEN-index);
279         }
280         if (opts & PGM_OPT_PARITY){
281                 returned_length = g_snprintf(&msg[index], MAX_STR_LEN-index, "%sParity", (!index)?"":",");
282                 index += MIN(returned_length, MAX_STR_LEN-index);
283         }
284         if (!index) {
285                 g_snprintf(&msg[index], MAX_STR_LEN-index, "0x%x", opts);
286         }
287         return(msg);
288 }
289 static const char *
290 paritystr(guint8 parity)
291 {
292         char *msg;
293         size_t returned_length, index = 0;
294         const int MAX_STR_LEN = 256;
295
296         if (parity == 0)
297                 return("");
298
299         msg=ep_alloc(MAX_STR_LEN);
300         if (parity & PGM_OPT_PARITY_PRM_PRO){
301                 returned_length = g_snprintf(&msg[index], MAX_STR_LEN-index, "Pro-active");
302                 index += MIN(returned_length, MAX_STR_LEN-index);
303         }
304         if (parity & PGM_OPT_PARITY_PRM_OND){
305                 returned_length = g_snprintf(&msg[index], MAX_STR_LEN-index, "%sOn-demand", (!index)?"":",");
306                 index += MIN(returned_length, MAX_STR_LEN-index);
307         }
308         if (!index) {
309                 g_snprintf(&msg[index], MAX_STR_LEN-index, "0x%x", parity);
310         }
311         return(msg);
312 }
313
314 static const value_string opt_vals[] = {
315         { PGM_OPT_LENGTH,      "Length" },
316         { PGM_OPT_END,         "End" },
317         { PGM_OPT_FRAGMENT,    "Fragment" },
318         { PGM_OPT_NAK_LIST,    "NakList" },
319         { PGM_OPT_JOIN,        "Join" },
320         { PGM_OPT_REDIRECT,    "ReDirect" },
321         { PGM_OPT_SYN,         "Syn" },
322         { PGM_OPT_FIN,         "Fin" },
323         { PGM_OPT_RST,         "Rst" },
324         { PGM_OPT_PARITY_PRM,  "ParityPrm" },
325         { PGM_OPT_PARITY_GRP,  "ParityGrp" },
326         { PGM_OPT_CURR_TGSIZE, "CurrTgsiz" },
327         { PGM_OPT_PGMCC_DATA,  "CcData" },
328         { PGM_OPT_PGMCC_FEEDBACK, "CcFeedBack" },
329         { PGM_OPT_NAK_BO_IVL,  "NakBackOffIvl" },
330         { PGM_OPT_NAK_BO_RNG,  "NakBackOffRng" },
331         { PGM_OPT_FRAGMENT,    "Fragment" },
332         { 0,                   NULL }
333 };
334
335 static const value_string opx_vals[] = {
336         { PGM_OPX_IGNORE,  "Ignore" },
337         { PGM_OPX_INVAL,   "Inval" },
338         { PGM_OPX_DISCARD, "DisCard" },
339         { 0,               NULL }
340 };
341
342 static const true_false_string opts_present = {
343         "Present",
344         "Not Present"
345 };
346
347 static void
348 dissect_pgmopts(ptvcursor_t* cursor, const char *pktname)
349 {
350         proto_item *tf;
351         proto_tree *opts_tree = NULL;
352         proto_tree *opt_tree = NULL;
353         tvbuff_t *tvb = ptvcursor_tvbuff(cursor);
354
355         gboolean theend = FALSE;
356
357         guint16 opts_total_len;
358         guint8 genopts_type;
359         guint8 genopts_len;
360         guint8 opts_type = tvb_get_guint8(tvb, ptvcursor_current_offset(cursor));
361
362         if (opts_type != PGM_OPT_LENGTH) {
363                 proto_tree_add_text(ptvcursor_tree(cursor), tvb, ptvcursor_current_offset(cursor), 1,
364                     "%s Options - initial option is %s, should be %s",
365                     pktname,
366                     val_to_str(opts_type, opt_vals, "Unknown (0x%02x)"),
367                     val_to_str(PGM_OPT_LENGTH, opt_vals, "Unknown (0x%02x)"));
368                 return;
369         }
370
371         opts_total_len = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor)+2);
372         
373         if (opts_total_len < 4) {
374                 proto_tree_add_text(opts_tree, tvb, ptvcursor_current_offset(cursor)+2, 2,
375                         "%s Options (Total Length %u - invalid, must be >= 4)",
376                         pktname, opts_total_len);
377                 return;
378         }
379         
380         tf = proto_tree_add_text(ptvcursor_tree(cursor), tvb, ptvcursor_current_offset(cursor), opts_total_len,
381                 "%s Options (Total Length %d)", pktname, opts_total_len);
382         opts_tree = proto_item_add_subtree(tf, ett_pgm_opts);
383         ptvcursor_set_tree(cursor, opts_tree);
384         ptvcursor_add(cursor, hf_pgm_opt_type, 1, FALSE);
385         ptvcursor_add(cursor, hf_pgm_opt_len, 1, FALSE);
386         ptvcursor_add(cursor, hf_pgm_opt_tlen, 2, FALSE);
387
388         for (opts_total_len -= 4; !theend && opts_total_len != 0;){
389                 if (opts_total_len < 4) {
390                         proto_tree_add_text(opts_tree, tvb, ptvcursor_current_offset(cursor), opts_total_len,
391                             "Remaining total options length doesn't have enough for an options header");
392                         break;
393                 }
394
395                 genopts_type = tvb_get_guint8(tvb, ptvcursor_current_offset(cursor));
396                 genopts_len = tvb_get_guint8(tvb, ptvcursor_current_offset(cursor)+1);
397
398                 if (genopts_type & PGM_OPT_END)  {
399                         genopts_type &= ~PGM_OPT_END;
400                         theend = TRUE;
401                 }
402                 if (genopts_len < 4) {
403                         proto_tree_add_text(opts_tree, tvb, ptvcursor_current_offset(cursor), genopts_len,
404                                 "Option: %s, Length: %u (invalid, must be >= 4)",
405                                 val_to_str(genopts_type, opt_vals, "Unknown (0x%02x)"),
406                                 genopts_len);
407                         break;
408                 }
409                 if (opts_total_len < genopts_len) {
410                         proto_tree_add_text(opts_tree, tvb, ptvcursor_current_offset(cursor), genopts_len,
411                             "Option: %s, Length: %u (> remaining total options length)",
412                             val_to_str(genopts_type, opt_vals, "Unknown (0x%02x)"),
413                             genopts_len);
414                         break;
415                 }
416                 tf = proto_tree_add_text(opts_tree, tvb, ptvcursor_current_offset(cursor), genopts_len,
417                         "Option: %s, Length: %u",
418                         val_to_str(genopts_type, opt_vals, "Unknown (0x%02x)"),
419                         genopts_len);
420
421                 switch(genopts_type) {
422                 case PGM_OPT_JOIN:{
423                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_join);
424                         ptvcursor_set_tree(cursor, opt_tree);
425
426                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
427
428                         if (genopts_len < PGM_OPT_JOIN_SIZE) {
429                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
430                                         ptvcursor_current_offset(cursor), 1, genopts_len,
431                                         "Length: %u (bogus, must be >= %u)",
432                                         genopts_len, PGM_OPT_JOIN_SIZE);
433                                 break;
434                         }
435                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
436                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
437                         ptvcursor_add(cursor, hf_pgm_opt_join_res, 1, FALSE);
438                         ptvcursor_add(cursor, hf_pgm_opt_join_minjoin, 4, FALSE);
439
440                         break;
441                 }
442                 case PGM_OPT_PARITY_PRM:{
443                         guint8 optdata_po;
444
445                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_parityprm);
446                         ptvcursor_set_tree(cursor, opt_tree);
447
448                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
449                         
450                         if (genopts_len < PGM_OPT_PARITY_PRM_SIZE) {
451                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, ptvcursor_tvbuff(cursor),
452                                         ptvcursor_current_offset(cursor), 1, genopts_len,
453                                         "Length: %u (bogus, must be >= %u)",
454                                         genopts_len, PGM_OPT_PARITY_PRM_SIZE);
455                                 break;
456                         }
457                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
458                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
459                         optdata_po = tvb_get_guint8(tvb, ptvcursor_current_offset(cursor));
460                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_parity_prm_po, tvb,
461                                 ptvcursor_current_offset(cursor), 1, optdata_po, "Parity Parameters: %s (0x%x)",
462                                 paritystr(optdata_po), optdata_po);
463                         ptvcursor_advance(cursor, 1);
464
465                         ptvcursor_add(cursor, hf_pgm_opt_parity_prm_prmtgsz, 4, FALSE);
466
467                         break;
468                 }
469                 case PGM_OPT_PARITY_GRP:{
470                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_paritygrp);
471                         ptvcursor_set_tree(cursor, opt_tree);
472
473                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
474
475                         if (genopts_len < PGM_OPT_PARITY_GRP_SIZE) {
476                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
477                                         ptvcursor_current_offset(cursor), 1, genopts_len,
478                                         "Length: %u (bogus, must be >= %u)",
479                                         genopts_len, PGM_OPT_PARITY_GRP_SIZE);
480                                 break;
481                         }
482                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
483                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
484                         ptvcursor_add(cursor, hf_pgm_opt_parity_grp_res, 1, FALSE);
485                         ptvcursor_add(cursor, hf_pgm_opt_parity_grp_prmgrp, 4, FALSE);
486
487                         break;
488                 }
489                 case PGM_OPT_NAK_LIST:{
490                         guint8 optdata_len;
491                         guint32 naklist[PGM_MAX_NAK_LIST_SZ+1];
492                         unsigned char *nakbuf;
493                         gboolean firsttime;
494                         int i, j, naks, soffset;
495
496                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_naklist);
497                         ptvcursor_set_tree(cursor, opt_tree);
498
499                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
500                         
501                         optdata_len = tvb_get_guint8(tvb, ptvcursor_current_offset(cursor));
502                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
503                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
504                         ptvcursor_add(cursor, hf_pgm_opt_nak_res, 1, FALSE);
505                         
506                         optdata_len -= PGM_OPT_NAK_LIST_SIZE;
507                         tvb_memcpy(tvb, (guint8 *)naklist, ptvcursor_current_offset(cursor), optdata_len);
508                         firsttime = TRUE;
509                         soffset = 0;
510                         naks = (optdata_len/sizeof(guint32));
511                         nakbuf = ep_alloc(8192);
512                         j = 0;
513                         /*
514                          * Print out 8 per line
515                          */
516                         for (i=0; i < naks; i++) {
517                                 soffset += MIN(8192-soffset,
518                                         g_snprintf(nakbuf+soffset, 8192-soffset, "0x%lx ",
519                                                 (unsigned long)g_ntohl(naklist[i])));
520                                 if ((++j % 8) == 0) {
521                                         if (firsttime) {
522                                                 proto_tree_add_bytes_format(opt_tree,
523                                                         hf_pgm_opt_nak_list, tvb, ptvcursor_current_offset(cursor), j*4,
524                                                         nakbuf, "List(%d): %s", naks, nakbuf);
525                                                 soffset = 0;
526                                                 firsttime = FALSE;
527                                         } else {
528                                                 proto_tree_add_bytes_format(opt_tree,
529                                                         hf_pgm_opt_nak_list, tvb, ptvcursor_current_offset(cursor), j*4,
530                                                         nakbuf, "List: %s", nakbuf);
531                                                 soffset = 0;
532                                         }
533                                         ptvcursor_advance(cursor, j*4);
534                                         j = 0;
535                                 }
536                         }
537                         if (j) {
538                                 if (firsttime) {
539                                         proto_tree_add_bytes_format(opt_tree,
540                                                 hf_pgm_opt_nak_list, tvb, ptvcursor_current_offset(cursor), j*4,
541                                                 nakbuf, "List(%d): %s", naks, nakbuf);
542                                 } else {
543                                         proto_tree_add_bytes_format(opt_tree,
544                                                 hf_pgm_opt_nak_list, tvb, ptvcursor_current_offset(cursor), j*4,
545                                                 nakbuf, "List: %s", nakbuf);
546                                 }
547                                 ptvcursor_advance(cursor, j*4);
548                         }
549                         break;
550                 }
551                 case PGM_OPT_PGMCC_DATA:{
552                         guint16 optdata_afi;
553
554                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_ccdata);
555                         ptvcursor_set_tree(cursor, opt_tree);
556
557                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
558
559                         if (genopts_len < PGM_OPT_PGMCC_DATA_SIZE) {
560                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
561                                         ptvcursor_current_offset(cursor), 1, genopts_len,
562                                         "Length: %u (bogus, must be >= %u)",
563                                         genopts_len, PGM_OPT_PGMCC_DATA_SIZE);
564                                 break;
565                         }
566                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
567                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
568                         ptvcursor_add(cursor, hf_pgm_opt_ccdata_res, 1, FALSE);
569                         ptvcursor_add(cursor, hf_pgm_opt_ccdata_tsp, 4, FALSE);
570                         optdata_afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor));
571                         ptvcursor_add(cursor, hf_pgm_opt_ccdata_afi, 1, FALSE);
572                         ptvcursor_add(cursor, hf_pgm_opt_ccdata_res2, 1, FALSE);
573
574                         switch (optdata_afi) {
575
576                         case AFNUM_INET:
577                                 ptvcursor_add(cursor, hf_pgm_opt_ccdata_acker, 4, FALSE);
578                                 break;
579
580                         case AFNUM_INET6:
581                                 ptvcursor_add(cursor, hf_pgm_opt_ccdata_acker6, 16, FALSE);
582                                 break;
583
584                         default:
585                                 proto_tree_add_text(opt_tree, tvb, ptvcursor_current_offset(cursor), -1, 
586                                     "Can't handle this address format");
587                                 break;
588                         }
589
590                         break;
591                 }
592                 case PGM_OPT_PGMCC_FEEDBACK:{
593                         guint16 optdata_afi;
594
595                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_ccdata);
596                         ptvcursor_set_tree(cursor, opt_tree);
597
598                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
599
600                         if (genopts_len < PGM_OPT_PGMCC_FEEDBACK_SIZE) {
601                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
602                                         ptvcursor_current_offset(cursor), 1, genopts_len,
603                                         "Length: %u (bogus, must be >= %u)",
604                                         genopts_len, PGM_OPT_PGMCC_FEEDBACK_SIZE);
605                                 break;
606                         }
607                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
608                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
609                         ptvcursor_add(cursor, hf_pgm_opt_ccfeedbk_res, 1, FALSE);
610                         ptvcursor_add(cursor, hf_pgm_opt_ccfeedbk_tsp, 4, FALSE);
611                         optdata_afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor));
612                         ptvcursor_add(cursor, hf_pgm_opt_ccfeedbk_afi, 2, FALSE);
613                         ptvcursor_add(cursor, hf_pgm_opt_ccfeedbk_lossrate, 2, FALSE);
614
615                         switch (optdata_afi) {
616
617                         case AFNUM_INET:
618                                 ptvcursor_add(cursor, hf_pgm_opt_ccfeedbk_acker, 4, FALSE);
619                                 break;
620
621                         case AFNUM_INET6:
622                                 ptvcursor_add(cursor, hf_pgm_opt_ccfeedbk_acker6, 16, FALSE);
623                                 break;
624
625                         default:
626                                 proto_tree_add_text(opt_tree, tvb, ptvcursor_current_offset(cursor), -1,
627                                     "Can't handle this address format");
628                                 break;
629                         }
630
631                         break;
632                 }
633                 case PGM_OPT_NAK_BO_IVL:{
634                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_nak_bo_ivl);
635                         ptvcursor_set_tree(cursor, opt_tree);
636
637                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
638
639                         if (genopts_len < PGM_OPT_NAK_BO_IVL_SIZE) {
640                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
641                                         ptvcursor_current_offset(cursor), 1, genopts_len,
642                                         "Length: %u (bogus, must be >= %u)",
643                                         genopts_len, PGM_OPT_NAK_BO_IVL_SIZE);
644                                 break;
645                         }
646                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
647                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
648                         ptvcursor_add(cursor, hf_pgm_opt_nak_bo_ivl_res, 1, FALSE);
649                         ptvcursor_add(cursor, hf_pgm_opt_nak_bo_ivl_bo_ivl, 1, FALSE);
650                         ptvcursor_add(cursor, hf_pgm_opt_nak_bo_ivl_bo_ivl_sqn, 1, FALSE);
651
652                         break;
653                 }
654                 case PGM_OPT_NAK_BO_RNG:{
655                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_nak_bo_rng);
656                         ptvcursor_set_tree(cursor, opt_tree);
657
658                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
659
660                         if (genopts_len < PGM_OPT_NAK_BO_RNG_SIZE) {
661                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
662                                         ptvcursor_current_offset(cursor), 1, genopts_len,
663                                         "Length: %u (bogus, must be >= %u)",
664                                         genopts_len, PGM_OPT_NAK_BO_RNG_SIZE);
665                                 break;
666                         }
667                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
668                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
669                         ptvcursor_add(cursor, hf_pgm_opt_nak_bo_rng_res, 1, FALSE);
670                         ptvcursor_add(cursor, hf_pgm_opt_nak_bo_rng_min_bo_ivl, 4, FALSE);
671                         ptvcursor_add(cursor, hf_pgm_opt_nak_bo_rng_max_bo_ivl, 4, FALSE);
672
673                         break;
674                 }
675                 case PGM_OPT_REDIRECT:{
676                         guint16 optdata_afi;
677
678                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_redirect);
679                         ptvcursor_set_tree(cursor, opt_tree);
680
681                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
682
683                         if (genopts_len < PGM_OPT_REDIRECT_SIZE) {
684                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
685                                         ptvcursor_current_offset(cursor), 1, genopts_len,
686                                         "Length: %u (bogus, must be >= %u)",
687                                         genopts_len, PGM_OPT_REDIRECT_SIZE);
688                                 break;
689                         }
690                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
691                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
692                         ptvcursor_add(cursor, hf_pgm_opt_redirect_res, 1, FALSE);
693                         optdata_afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor));
694                         ptvcursor_add(cursor, hf_pgm_opt_redirect_afi, 1, FALSE);
695                         ptvcursor_add(cursor, hf_pgm_opt_redirect_res2, 1, FALSE);
696
697                         switch (optdata_afi) {
698
699                         case AFNUM_INET:
700                                 ptvcursor_add(cursor, hf_pgm_opt_redirect_dlr, 4, FALSE);
701                                 break;
702
703                         case AFNUM_INET6:
704                                 ptvcursor_add(cursor, hf_pgm_opt_redirect_dlr6, 16, FALSE);
705                                 break;
706
707                         default:
708                                 proto_tree_add_text(opt_tree, tvb, ptvcursor_current_offset(cursor), -1, 
709                                     "Can't handle this address format");
710                                 break;
711                         }
712
713                         break;
714                 }
715                 case PGM_OPT_FRAGMENT:{
716                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_fragment);
717                         ptvcursor_set_tree(cursor, opt_tree);
718
719                         ptvcursor_add(cursor, hf_pgm_genopt_type, 1, FALSE);
720
721                         if (genopts_len < PGM_OPT_FRAGMENT_SIZE) {
722                                 proto_tree_add_uint_format(opt_tree, hf_pgm_genopt_len, tvb,
723                                         ptvcursor_current_offset(cursor), 1, genopts_len,
724                                         "Length: %u (bogus, must be >= %u)",
725                                         genopts_len, PGM_OPT_FRAGMENT_SIZE);
726                                 break;
727                         }
728                         ptvcursor_add(cursor, hf_pgm_genopt_len, 1, FALSE);
729                         ptvcursor_add(cursor, hf_pgm_genopt_opx, 1, FALSE);
730                         ptvcursor_add(cursor, hf_pgm_opt_fragment_res, 1, FALSE);
731                         ptvcursor_add(cursor, hf_pgm_opt_fragment_first_sqn, 4, FALSE);
732                         ptvcursor_add(cursor, hf_pgm_opt_fragment_offset, 4, FALSE);
733                         ptvcursor_add(cursor, hf_pgm_opt_fragment_total_length, 4, FALSE);
734
735                         break;
736                 }
737                 }
738
739                 opts_total_len -= genopts_len;
740         }
741         return;
742 }
743
744 static const value_string type_vals[] = {
745         { PGM_SPM_PCKT,   "SPM" },
746         { PGM_RDATA_PCKT, "RDATA" },
747         { PGM_ODATA_PCKT, "ODATA" },
748         { PGM_NAK_PCKT,   "NAK" },
749         { PGM_NNAK_PCKT,  "NNAK" },
750         { PGM_NCF_PCKT,   "NCF" },
751         { PGM_POLL_PCKT,  "POLL" },
752         { PGM_POLR_PCKT,  "POLR" },
753         { PGM_ACK_PCKT,   "ACK" },
754         { 0,              NULL }
755 };
756
757 static const value_string poll_subtype_vals[] = {
758         { PGM_POLL_GENERAL,   "General" },
759         { PGM_POLL_DLR,       "DLR" },
760         { 0,                  NULL }
761 };
762
763 /* Determine if there is a sub-dissector and call it.  This has been */
764 /* separated into a stand alone routine to other protocol dissectors */
765 /* can call to it, ie. socks    */
766
767 static void
768 decode_pgm_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
769         proto_tree *tree, guint16 pgmhdr_sport, guint16 pgmhdr_dport)
770 {
771   tvbuff_t *next_tvb;
772   int found = 0;
773
774   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
775
776   /* do lookup with the subdissector table */
777   found = dissector_try_port(subdissector_table, pgmhdr_sport,
778                         next_tvb, pinfo, tree);
779   if (found)
780         return;
781
782   found = dissector_try_port(subdissector_table, pgmhdr_dport,
783                         next_tvb, pinfo, tree);
784   if (found)
785         return;
786
787   /* do lookup with the heuristic subdissector table */
788   if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
789         return;
790
791   /* Oh, well, we don't know this; dissect it as data. */
792   call_dissector(data_handle,next_tvb, pinfo, tree);
793 }
794
795 /*
796  * dissect_pgm - The dissector for Pragmatic General Multicast
797  */
798 static void
799 dissect_pgm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
800 {
801         guint16 pgmhdr_sport;
802         guint16 pgmhdr_dport;
803         guint8 pgmhdr_type;
804         guint8 pgmhdr_opts;
805         guint16 pgmhdr_cksum;
806         guint16 pgmhdr_tsdulen;
807         guint32 sqn;
808         guint16 afi;
809
810         guint plen = 0;
811         proto_item *ti;
812         const char *pktname;
813         const char *pollstname;
814         char *gsi;
815         gboolean isdata = FALSE;
816         guint pgmlen, reportedlen;
817
818         if (check_col(pinfo->cinfo, COL_PROTOCOL))
819                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "PGM");
820
821         if (check_col(pinfo->cinfo, COL_INFO)) {
822                 col_clear(pinfo->cinfo, COL_INFO);
823                 if (tvb_reported_length_remaining(tvb, 0) < 18) {
824                         col_set_str(pinfo->cinfo, COL_INFO,
825                                 "Packet too small");
826                         return;
827                 }
828         }
829
830         pinfo->srcport = pgmhdr_sport = tvb_get_ntohs(tvb, 0);
831         pinfo->destport = pgmhdr_dport = tvb_get_ntohs(tvb, 2);
832
833         pgmhdr_type = tvb_get_guint8(tvb, 4);
834         pktname = val_to_str(pgmhdr_type, type_vals, "Unknown (0x%02x)");
835
836         pgmhdr_opts = tvb_get_guint8(tvb, 5);
837         pgmhdr_cksum = tvb_get_ntohs(tvb, 6);
838         gsi = tvb_bytes_to_str(tvb, 8, 6);
839         pgmhdr_tsdulen = tvb_get_ntohs(tvb, 14);
840         sqn = tvb_get_ntohl(tvb, 16);
841
842         switch(pgmhdr_type) {
843         case PGM_SPM_PCKT:
844         case PGM_NAK_PCKT:
845         case PGM_NNAK_PCKT:
846         case PGM_NCF_PCKT:
847         case PGM_POLR_PCKT:
848         case PGM_ACK_PCKT:
849                 if (check_col(pinfo->cinfo, COL_INFO)) {
850                         col_add_fstr(pinfo->cinfo, COL_INFO,
851                                 "%-5s sqn 0x%x gsi %s", pktname, sqn, gsi);
852                 }
853                 break;
854         case PGM_RDATA_PCKT:
855         case PGM_ODATA_PCKT:
856                 if (check_col(pinfo->cinfo, COL_INFO)) {
857                         col_add_fstr(pinfo->cinfo, COL_INFO,
858                             "%-5s sqn 0x%x gsi %s tsdulen %d", pktname, sqn, gsi,
859                             pgmhdr_tsdulen);
860                 }
861                 isdata = TRUE;
862                 break;
863         case PGM_POLL_PCKT: {
864                 guint16 poll_stype = tvb_get_ntohs(tvb, 22);
865                 pollstname = val_to_str(poll_stype, poll_subtype_vals, "Unknown (0x%02x)");
866
867                 if (check_col(pinfo->cinfo, COL_INFO)) {
868                         col_add_fstr(pinfo->cinfo, COL_INFO,
869                                 "%-5s sqn 0x%x gsi %s subtype %s", 
870                                         pktname, sqn, gsi, pollstname);
871                 }
872                 }
873                 break;
874         default:
875                 return;
876         }
877
878         {
879                 proto_tree *pgm_tree = NULL;
880                 proto_tree *opt_tree = NULL;
881                 proto_tree *type_tree = NULL;
882                 proto_item *tf, *hidden_item;
883                 ptvcursor_t* cursor;
884
885                 ti = proto_tree_add_protocol_format(tree, proto_pgm,
886                         tvb, 0, -1,
887                         "Pragmatic General Multicast: Type %s"
888                             " Src Port %u, Dst Port %u, GSI %s", pktname,
889                         pgmhdr_sport, pgmhdr_dport, gsi);
890
891                 pgm_tree = proto_item_add_subtree(ti, ett_pgm);
892
893                 cursor = ptvcursor_new(pgm_tree, tvb, 0);
894
895                 hidden_item = proto_tree_add_item(pgm_tree, hf_pgm_port, tvb, 0, 2, FALSE);
896                 PROTO_ITEM_SET_HIDDEN(hidden_item);
897                 hidden_item = proto_tree_add_item(pgm_tree, hf_pgm_port, tvb, 2, 2, FALSE);
898                 PROTO_ITEM_SET_HIDDEN(hidden_item);
899                 ptvcursor_add(cursor, hf_pgm_main_sport, 2, FALSE);
900                 ptvcursor_add(cursor, hf_pgm_main_dport, 2, FALSE);
901                 ptvcursor_add(cursor, hf_pgm_main_type, 1, FALSE);
902
903                 tf = proto_tree_add_uint_format(pgm_tree, hf_pgm_main_opts, tvb,
904                         ptvcursor_current_offset(cursor), 1, pgmhdr_opts, "Options: %s (0x%x)",
905                         optsstr(pgmhdr_opts), pgmhdr_opts);
906                 opt_tree = proto_item_add_subtree(tf, ett_pgm_optbits);
907                 ptvcursor_set_tree(cursor, opt_tree);
908
909                 ptvcursor_add_no_advance(cursor, hf_pgm_main_opts_opt, 1, FALSE);
910                 ptvcursor_add_no_advance(cursor, hf_pgm_main_opts_netsig, 1, FALSE);
911                 ptvcursor_add_no_advance(cursor, hf_pgm_main_opts_varlen, 1, FALSE);
912                 ptvcursor_add(cursor, hf_pgm_main_opts_parity, 1, FALSE);
913                 ptvcursor_set_tree(cursor, pgm_tree);
914
915                 /* Checksum may be 0 (not available), but not for DATA packets */
916                 if ((pgmhdr_type != PGM_RDATA_PCKT) && (pgmhdr_type != PGM_ODATA_PCKT) && 
917                     (pgmhdr_cksum == 0))
918                 {
919                         proto_tree_add_uint_format(pgm_tree, hf_pgm_main_cksum, tvb,
920                                 ptvcursor_current_offset(cursor), 2, pgmhdr_cksum, "Checksum: not available");
921                 } else {
922                         reportedlen = tvb_reported_length(tvb);
923                         pgmlen = tvb_length(tvb);
924                         if (pgm_check_checksum && pgmlen >= reportedlen) {
925                                 vec_t cksum_vec[1];
926                                 guint16 computed_cksum;
927
928                                 cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pgmlen);
929                                 cksum_vec[0].len = pgmlen;
930                                 computed_cksum = in_cksum(&cksum_vec[0], 1);
931                                 if (computed_cksum == 0) {
932                                         proto_tree_add_uint_format(pgm_tree, hf_pgm_main_cksum, tvb,
933                                                 ptvcursor_current_offset(cursor), 2, pgmhdr_cksum, "Checksum: 0x%04x [correct]", pgmhdr_cksum);
934                                 } else {
935                                         hidden_item = proto_tree_add_boolean(pgm_tree, hf_pgm_main_cksum_bad, tvb,
936                                             ptvcursor_current_offset(cursor), 2, TRUE);
937                                         PROTO_ITEM_SET_HIDDEN(hidden_item);
938                                         proto_tree_add_uint_format(pgm_tree, hf_pgm_main_cksum, tvb,
939                                             ptvcursor_current_offset(cursor), 2, pgmhdr_cksum, "Checksum: 0x%04x [incorrect, should be 0x%04x]",
940                                                 pgmhdr_cksum, in_cksum_shouldbe(pgmhdr_cksum, computed_cksum));
941                                 }
942                         } else {
943                                 ptvcursor_add_no_advance(cursor, hf_pgm_main_cksum, 2, FALSE);
944                         }
945                 }
946                 ptvcursor_advance(cursor, 2);
947
948                 ptvcursor_add(cursor, hf_pgm_main_gsi, 6, FALSE);
949                 ptvcursor_add(cursor, hf_pgm_main_tsdulen, 2, FALSE);
950
951                 tf = proto_tree_add_text(pgm_tree, tvb, ptvcursor_current_offset(cursor), plen, "%s Packet", pktname);
952                 switch(pgmhdr_type) {
953                 case PGM_SPM_PCKT:
954                         type_tree = proto_item_add_subtree(tf, ett_pgm_spm);
955                         ptvcursor_set_tree(cursor, type_tree);
956
957                         ptvcursor_add(cursor, hf_pgm_spm_sqn, 4, FALSE);
958                         ptvcursor_add(cursor, hf_pgm_spm_trail, 4, FALSE);
959                         ptvcursor_add(cursor, hf_pgm_spm_lead, 4, FALSE);
960                         afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor));
961                         ptvcursor_add(cursor, hf_pgm_spm_pathafi, 2, FALSE);
962                         ptvcursor_add(cursor, hf_pgm_spm_res, 2, FALSE);
963
964                         switch (afi) {
965                         case AFNUM_INET:
966                                 ptvcursor_add(cursor, hf_pgm_spm_path, 4, FALSE);
967                                 break;
968
969                         case AFNUM_INET6:
970                                 ptvcursor_add(cursor, hf_pgm_spm_path6, 16, FALSE);
971                                 break;
972
973                         default:
974                                 proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, 
975                                     "Can't handle this address format");
976                                 return;
977                         }
978                         break;
979                 case PGM_RDATA_PCKT:
980                 case PGM_ODATA_PCKT:
981                         type_tree = proto_item_add_subtree(tf, ett_pgm_data);
982                         ptvcursor_set_tree(cursor, type_tree);
983
984                         ptvcursor_add(cursor, hf_pgm_spm_sqn, 4, FALSE);
985                         ptvcursor_add(cursor, hf_pgm_spm_trail, 4, FALSE);
986                         break;
987                 case PGM_NAK_PCKT:
988                 case PGM_NNAK_PCKT:
989                 case PGM_NCF_PCKT:
990                         type_tree = proto_item_add_subtree(tf, ett_pgm_nak);
991                         ptvcursor_set_tree(cursor, type_tree);
992
993                         ptvcursor_add(cursor, hf_pgm_nak_sqn, 4, FALSE);
994                         afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor));
995                         ptvcursor_add(cursor, hf_pgm_nak_srcafi, 2, FALSE);
996                         ptvcursor_add(cursor, hf_pgm_nak_srcres, 2, FALSE);
997
998                         switch (afi) {
999                         case AFNUM_INET:
1000                                 ptvcursor_add(cursor, hf_pgm_nak_src, 4, FALSE);
1001                                 break;
1002
1003                         case AFNUM_INET6:
1004                                 ptvcursor_add(cursor, hf_pgm_nak_src6, 16, FALSE);
1005                                 break;
1006
1007                         default:
1008                                 proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, 
1009                                     "Can't handle this address format");
1010                                 break;
1011                         }
1012
1013                         afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor));
1014                         ptvcursor_add(cursor, hf_pgm_nak_grpafi, 2, FALSE);
1015                         ptvcursor_add(cursor, hf_pgm_nak_grpres, 2, FALSE);
1016
1017                         switch (afi) {
1018                         case AFNUM_INET:
1019                                 ptvcursor_add(cursor, hf_pgm_nak_grp, 4, FALSE);
1020                                 break;
1021
1022                         case AFNUM_INET6:
1023                                 ptvcursor_add(cursor, hf_pgm_nak_grp6, 16, FALSE);
1024                                 break;
1025
1026                         default:
1027                                 proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, 
1028                                     "Can't handle this address format");
1029                                 return;
1030                         }
1031                         break;
1032                 case PGM_POLL_PCKT:
1033                         type_tree = proto_item_add_subtree(tf, ett_pgm_poll);
1034                         ptvcursor_set_tree(cursor, type_tree);
1035
1036                         ptvcursor_add(cursor, hf_pgm_poll_sqn, 4, FALSE);
1037                         ptvcursor_add(cursor, hf_pgm_poll_round, 2, FALSE);
1038                         ptvcursor_add(cursor, hf_pgm_poll_subtype, 2, FALSE);
1039                         afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor));
1040                         ptvcursor_add(cursor, hf_pgm_poll_pathafi, 2, FALSE);
1041                         ptvcursor_add(cursor, hf_pgm_poll_res, 2, FALSE);
1042
1043                         switch (afi) {
1044                         case AFNUM_INET:
1045                                 ptvcursor_add(cursor, hf_pgm_poll_path, 4, FALSE);
1046                                 break;
1047
1048                         case AFNUM_INET6:
1049                                 ptvcursor_add(cursor, hf_pgm_poll_path6, 16, FALSE);
1050                                 break;
1051
1052                         default:
1053                                 proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, 
1054                                     "Can't handle this address format");
1055                                 break;
1056                         }
1057
1058                         ptvcursor_add(cursor, hf_pgm_poll_backoff_ivl, 4, FALSE);
1059                         ptvcursor_add(cursor, hf_pgm_poll_rand_str, 4, FALSE);
1060                         ptvcursor_add(cursor, hf_pgm_poll_matching_bmask, 4, FALSE);
1061                         break;
1062                 case PGM_POLR_PCKT:
1063                         type_tree = proto_item_add_subtree(tf, ett_pgm_polr);
1064                         ptvcursor_set_tree(cursor, type_tree);
1065
1066                         ptvcursor_add(cursor, hf_pgm_polr_sqn, 4, FALSE);
1067                         ptvcursor_add(cursor, hf_pgm_polr_round, 2, FALSE);
1068                         ptvcursor_add(cursor, hf_pgm_polr_res, 2, FALSE);
1069                         break;
1070                 case PGM_ACK_PCKT:
1071                         type_tree = proto_item_add_subtree(tf, ett_pgm_ack);
1072                         ptvcursor_set_tree(cursor, type_tree);
1073
1074                         ptvcursor_add(cursor, hf_pgm_ack_sqn, 4, FALSE);
1075                         ptvcursor_add(cursor, hf_pgm_ack_bitmap, 4, FALSE);
1076                         break;
1077                 }
1078
1079                 if (pgmhdr_opts & PGM_OPT)
1080                         dissect_pgmopts(cursor, pktname);
1081
1082                 if (isdata) 
1083                         decode_pgm_ports(tvb, ptvcursor_current_offset(cursor), pinfo, tree, pgmhdr_sport, pgmhdr_dport);
1084         }
1085 }
1086
1087 /* Register all the bits needed with the filtering engine */
1088 void
1089 proto_register_pgm(void)
1090 {
1091   static hf_register_info hf[] = {
1092     { &hf_pgm_main_sport,
1093       { "Source Port", "pgm.hdr.sport", FT_UINT16, BASE_DEC,
1094           NULL, 0x0, "", HFILL }},
1095     { &hf_pgm_main_dport,
1096       { "Destination Port", "pgm.hdr.dport", FT_UINT16, BASE_DEC,
1097           NULL, 0x0, "", HFILL }},
1098     { &hf_pgm_port,
1099       { "Port", "pgm.port", FT_UINT16, BASE_DEC,
1100           NULL, 0x0, "", HFILL }},
1101     { &hf_pgm_main_type,
1102       { "Type", "pgm.hdr.type", FT_UINT8, BASE_HEX,
1103           VALS(type_vals), 0x0, "", HFILL }},
1104     { &hf_pgm_main_opts,
1105       { "Options", "pgm.hdr.opts", FT_UINT8, BASE_HEX,
1106           NULL, 0x0, "", HFILL }},
1107     { &hf_pgm_main_opts_opt,
1108       { "Options", "pgm.hdr.opts.opt", FT_BOOLEAN, 8,
1109           TFS(&opts_present), PGM_OPT, "", HFILL }},
1110     { &hf_pgm_main_opts_netsig,
1111       { "Network Significant Options", "pgm.hdr.opts.netsig",
1112           FT_BOOLEAN, 8,
1113           TFS(&opts_present), PGM_OPT_NETSIG, "", HFILL }},
1114     { &hf_pgm_main_opts_varlen,
1115       { "Variable length Parity Packet Option", "pgm.hdr.opts.varlen",
1116           FT_BOOLEAN, 8,
1117           TFS(&opts_present), PGM_OPT_VAR_PKTLEN, "", HFILL }},
1118     { &hf_pgm_main_opts_parity,
1119       { "Parity", "pgm.hdr.opts.parity", FT_BOOLEAN, 8,
1120           TFS(&opts_present), PGM_OPT_PARITY, "", HFILL }},
1121     { &hf_pgm_main_cksum,
1122       { "Checksum", "pgm.hdr.cksum", FT_UINT16, BASE_HEX,
1123         NULL, 0x0, "", HFILL }},
1124     { &hf_pgm_main_cksum_bad,
1125       { "Bad Checksum", "pgm.hdr.cksum_bad", FT_BOOLEAN, BASE_NONE,
1126         NULL, 0x0, "", HFILL }},
1127     { &hf_pgm_main_gsi,
1128       { "Global Source Identifier", "pgm.hdr.gsi", FT_BYTES, BASE_HEX,
1129           NULL, 0x0, "", HFILL }},
1130     { &hf_pgm_main_tsdulen,
1131       { "Transport Service Data Unit Length", "pgm.hdr.tsdulen", FT_UINT16,
1132           BASE_DEC, NULL, 0x0, "", HFILL }},
1133     { &hf_pgm_spm_sqn,
1134       { "Sequence number", "pgm.spm.sqn", FT_UINT32, BASE_HEX,
1135           NULL, 0x0, "", HFILL }},
1136     { &hf_pgm_spm_trail,
1137       { "Trailing Edge Sequence Number", "pgm.spm.trail", FT_UINT32, BASE_HEX,
1138           NULL, 0x0, "", HFILL }},
1139     { &hf_pgm_spm_lead,
1140       { "Leading Edge Sequence Number", "pgm.spm.lead", FT_UINT32, BASE_HEX,
1141           NULL, 0x0, "", HFILL }},
1142     { &hf_pgm_spm_pathafi,
1143       { "Path NLA AFI", "pgm.spm.pathafi", FT_UINT16, BASE_DEC,
1144           VALS(afn_vals), 0x0, "", HFILL }},
1145     { &hf_pgm_spm_res,
1146       { "Reserved", "pgm.spm.res", FT_UINT16, BASE_HEX,
1147           NULL, 0x0, "", HFILL }},
1148     { &hf_pgm_spm_path,
1149       { "Path NLA", "pgm.spm.path", FT_IPv4, BASE_NONE,
1150           NULL, 0x0, "", HFILL }},
1151     { &hf_pgm_spm_path6,
1152       { "Path NLA", "pgm.spm.path", FT_IPv6, BASE_NONE,
1153           NULL, 0x0, "", HFILL }},
1154     { &hf_pgm_data_sqn,
1155       { "Data Packet Sequence Number", "pgm.data.sqn", FT_UINT32, BASE_HEX,
1156           NULL, 0x0, "", HFILL }},
1157     { &hf_pgm_data_trail,
1158       { "Trailing Edge Sequence Number", "pgm.data.trail", FT_UINT32, BASE_HEX,
1159           NULL, 0x0, "", HFILL }},
1160     { &hf_pgm_nak_sqn,
1161       { "Requested Sequence Number", "pgm.nak.sqn", FT_UINT32, BASE_HEX,
1162           NULL, 0x0, "", HFILL }},
1163     { &hf_pgm_nak_srcafi,
1164       { "Source NLA AFI", "pgm.nak.srcafi", FT_UINT16, BASE_DEC,
1165           VALS(afn_vals), 0x0, "", HFILL }},
1166     { &hf_pgm_nak_srcres,
1167       { "Reserved", "pgm.nak.srcres", FT_UINT16, BASE_HEX,
1168           NULL, 0x0, "", HFILL }},
1169     { &hf_pgm_nak_src,
1170       { "Source NLA", "pgm.nak.src", FT_IPv4, BASE_NONE,
1171           NULL, 0x0, "", HFILL }},
1172     { &hf_pgm_nak_src6,
1173       { "Source NLA", "pgm.nak.src", FT_IPv6, BASE_NONE,
1174           NULL, 0x0, "", HFILL }},
1175     { &hf_pgm_nak_grpafi,
1176       { "Multicast Group AFI", "pgm.nak.grpafi", FT_UINT16, BASE_DEC,
1177           VALS(afn_vals), 0x0, "", HFILL }},
1178     { &hf_pgm_nak_grpres,
1179       { "Reserved", "pgm.nak.grpres", FT_UINT16, BASE_HEX,
1180           NULL, 0x0, "", HFILL }},
1181     { &hf_pgm_nak_grp,
1182       { "Multicast Group NLA", "pgm.nak.grp", FT_IPv4, BASE_NONE,
1183           NULL, 0x0, "", HFILL }},
1184     { &hf_pgm_nak_grp6,
1185       { "Multicast Group NLA", "pgm.nak.grp", FT_IPv6, BASE_NONE,
1186           NULL, 0x0, "", HFILL }},
1187     { &hf_pgm_poll_sqn,
1188       { "Sequence Number", "pgm.poll.sqn", FT_UINT32, BASE_HEX,
1189           NULL, 0x0, "", HFILL }},
1190     { &hf_pgm_poll_round,
1191       { "Round", "pgm.poll.round", FT_UINT16, BASE_DEC,
1192           NULL, 0x0, "", HFILL }},
1193     { &hf_pgm_poll_subtype,
1194       { "Subtype", "pgm.poll.subtype", FT_UINT16, BASE_HEX,
1195           VALS(poll_subtype_vals), 0x0, "", HFILL }},
1196     { &hf_pgm_poll_pathafi,
1197       { "Path NLA AFI", "pgm.poll.pathafi", FT_UINT16, BASE_DEC,
1198           VALS(afn_vals), 0x0, "", HFILL }},
1199     { &hf_pgm_poll_res,
1200       { "Reserved", "pgm.poll.res", FT_UINT16, BASE_HEX,
1201           NULL, 0x0, "", HFILL }},
1202     { &hf_pgm_poll_path,
1203       { "Path NLA", "pgm.poll.path", FT_IPv4, BASE_NONE,
1204           NULL, 0x0, "", HFILL }},
1205     { &hf_pgm_poll_path6,
1206       { "Path NLA", "pgm.poll.path", FT_IPv6, BASE_NONE,
1207           NULL, 0x0, "", HFILL }},
1208     { &hf_pgm_poll_backoff_ivl,
1209       { "Back-off Interval", "pgm.poll.backoff_ivl", FT_UINT32, BASE_DEC,
1210           NULL, 0x0, "", HFILL }},
1211     { &hf_pgm_poll_rand_str,
1212       { "Random String", "pgm.poll.rand_str", FT_UINT32, BASE_HEX,
1213           NULL, 0x0, "", HFILL }},
1214     { &hf_pgm_poll_matching_bmask,
1215       { "Matching Bitmask", "pgm.poll.matching_bmask", FT_UINT32, BASE_HEX,
1216           NULL, 0x0, "", HFILL }},
1217     { &hf_pgm_polr_sqn,
1218       { "Sequence Number", "pgm.polr.sqn", FT_UINT32, BASE_HEX,
1219           NULL, 0x0, "", HFILL }},
1220     { &hf_pgm_polr_round,
1221       { "Round", "pgm.polr.round", FT_UINT16, BASE_DEC,
1222           NULL, 0x0, "", HFILL }},
1223     { &hf_pgm_polr_res,
1224       { "Reserved", "pgm.polr.res", FT_UINT16, BASE_HEX,
1225           NULL, 0x0, "", HFILL }},
1226     { &hf_pgm_ack_sqn,
1227       { "Maximum Received Sequence Number", "pgm.ack.maxsqn", FT_UINT32,
1228           BASE_HEX, NULL, 0x0, "", HFILL }},
1229     { &hf_pgm_ack_bitmap,
1230       { "Packet Bitmap", "pgm.ack.bitmap", FT_UINT32, BASE_HEX,
1231           NULL, 0x0, "", HFILL }},
1232     { &hf_pgm_opt_type,
1233       { "Type", "pgm.opts.type", FT_UINT8, BASE_HEX,
1234           VALS(opt_vals), 0x0, "", HFILL }},
1235     { &hf_pgm_opt_len,
1236       { "Length", "pgm.opts.len", FT_UINT8, BASE_DEC,
1237           NULL, 0x0, "", HFILL }},
1238     { &hf_pgm_opt_tlen,
1239       { "Total Length", "pgm.opts.tlen", FT_UINT16, BASE_DEC,
1240           NULL, 0x0, "", HFILL }},
1241     { &hf_pgm_genopt_type,
1242       { "Type", "pgm.genopts.type", FT_UINT8, BASE_HEX,
1243           VALS(opt_vals), 0x0, "", HFILL }},
1244     { &hf_pgm_genopt_len,
1245       { "Length", "pgm.genopts.len", FT_UINT8, BASE_DEC,
1246           NULL, 0x0, "", HFILL }},
1247     { &hf_pgm_genopt_opx,
1248       { "Option Extensibility Bits", "pgm.genopts.opx", FT_UINT8, BASE_HEX,
1249           VALS(opx_vals), 0x0, "", HFILL }},
1250     { &hf_pgm_opt_parity_prm_po,
1251       { "Parity Parameters", "pgm.opts.parity_prm.op", FT_UINT8, BASE_HEX,
1252           NULL, 0x0, "", HFILL }},
1253     { &hf_pgm_opt_parity_prm_prmtgsz,
1254       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp",
1255           FT_UINT32, BASE_HEX,
1256           NULL, 0x0, "", HFILL }},
1257     { &hf_pgm_opt_join_res,
1258       { "Reserved", "pgm.opts.join.res", FT_UINT8, BASE_HEX,
1259           NULL, 0x0, "", HFILL }},
1260     { &hf_pgm_opt_join_minjoin,
1261       { "Minimum Sequence Number", "pgm.opts.join.min_join",
1262           FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1263     { &hf_pgm_opt_parity_grp_res,
1264       { "Reserved", "pgm.opts.parity_prm.op", FT_UINT8, BASE_HEX,
1265           NULL, 0x0, "", HFILL }},
1266     { &hf_pgm_opt_parity_grp_prmgrp,
1267       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp",
1268           FT_UINT32, BASE_HEX,
1269           NULL, 0x0, "", HFILL }},
1270     { &hf_pgm_opt_nak_res,
1271       { "Reserved", "pgm.opts.nak.op", FT_UINT8, BASE_HEX,
1272           NULL, 0x0, "", HFILL }},
1273     { &hf_pgm_opt_nak_list,
1274       { "List", "pgm.opts.nak.list", FT_BYTES, BASE_NONE,
1275           NULL, 0x0, "", HFILL }},
1276     { &hf_pgm_opt_ccdata_res,
1277       { "Reserved", "pgm.opts.ccdata.res", FT_UINT8, BASE_DEC,
1278           NULL, 0x0, "", HFILL }},
1279     { &hf_pgm_opt_ccdata_tsp,
1280       { "Time Stamp", "pgm.opts.ccdata.tstamp", FT_UINT16, BASE_HEX,
1281           NULL, 0x0, "", HFILL }},
1282     { &hf_pgm_opt_ccdata_afi,
1283       { "Acker AFI", "pgm.opts.ccdata.afi", FT_UINT16, BASE_DEC,
1284           VALS(afn_vals), 0x0, "", HFILL }},
1285     { &hf_pgm_opt_ccdata_res2,
1286       { "Reserved", "pgm.opts.ccdata.res2", FT_UINT16, BASE_DEC,
1287           NULL, 0x0, "", HFILL }},
1288     { &hf_pgm_opt_ccdata_acker,
1289       { "Acker", "pgm.opts.ccdata.acker", FT_IPv4, BASE_NONE,
1290           NULL, 0x0, "", HFILL }},
1291     { &hf_pgm_opt_ccdata_acker6,
1292       { "Acker", "pgm.opts.ccdata.acker", FT_IPv6, BASE_NONE,
1293           NULL, 0x0, "", HFILL }},
1294     { &hf_pgm_opt_ccfeedbk_res,
1295       { "Reserved", "pgm.opts.ccdata.res", FT_UINT8, BASE_DEC,
1296           NULL, 0x0, "", HFILL }},
1297     { &hf_pgm_opt_ccfeedbk_tsp,
1298       { "Time Stamp", "pgm.opts.ccdata.tstamp", FT_UINT16, BASE_HEX,
1299           NULL, 0x0, "", HFILL }},
1300     { &hf_pgm_opt_ccfeedbk_afi,
1301       { "Acker AFI", "pgm.opts.ccdata.afi", FT_UINT16, BASE_DEC,
1302           VALS(afn_vals), 0x0, "", HFILL }},
1303     { &hf_pgm_opt_ccfeedbk_lossrate,
1304       { "Loss Rate", "pgm.opts.ccdata.lossrate", FT_UINT16, BASE_HEX,
1305           NULL, 0x0, "", HFILL }},
1306     { &hf_pgm_opt_ccfeedbk_acker,
1307       { "Acker", "pgm.opts.ccdata.acker", FT_IPv4, BASE_NONE,
1308           NULL, 0x0, "", HFILL }},
1309     { &hf_pgm_opt_ccfeedbk_acker6,
1310       { "Acker", "pgm.opts.ccdata.acker", FT_IPv6, BASE_NONE,
1311           NULL, 0x0, "", HFILL }},
1312     { &hf_pgm_opt_nak_bo_ivl_res,
1313       { "Reserved", "pgm.opts.nak_bo_ivl.res", FT_UINT8, BASE_HEX,
1314           NULL, 0x0, "", HFILL }},
1315     { &hf_pgm_opt_nak_bo_ivl_bo_ivl,
1316       { "Back-off Interval", "pgm.opts.nak_bo_ivl.bo_ivl", FT_UINT32, BASE_DEC,
1317           NULL, 0x0, "", HFILL }},
1318     { &hf_pgm_opt_nak_bo_ivl_bo_ivl_sqn,
1319       { "Back-off Interval Sequence Number", "pgm.opts.nak_bo_ivl.bo_ivl_sqn", FT_UINT32, BASE_HEX,
1320           NULL, 0x0, "", HFILL }},
1321     { &hf_pgm_opt_nak_bo_rng_res,
1322       { "Reserved", "pgm.opts.nak_bo_rng.res", FT_UINT8, BASE_HEX,
1323           NULL, 0x0, "", HFILL }},
1324     { &hf_pgm_opt_nak_bo_rng_min_bo_ivl,
1325       { "Min Back-off Interval", "pgm.opts.nak_bo_rng.min_bo_ivl", FT_UINT32, BASE_DEC,
1326           NULL, 0x0, "", HFILL }},
1327     { &hf_pgm_opt_nak_bo_rng_max_bo_ivl,
1328       { "Max Back-off Interval", "pgm.opts.nak_bo_rng.max_bo_ivl", FT_UINT32, BASE_DEC,
1329           NULL, 0x0, "", HFILL }},
1330     { &hf_pgm_opt_redirect_res,
1331       { "Reserved", "pgm.opts.redirect.res", FT_UINT8, BASE_DEC,
1332           NULL, 0x0, "", HFILL }},
1333     { &hf_pgm_opt_redirect_afi,
1334       { "DLR AFI", "pgm.opts.redirect.afi", FT_UINT16, BASE_DEC,
1335           VALS(afn_vals), 0x0, "", HFILL }},
1336     { &hf_pgm_opt_redirect_res2,
1337       { "Reserved", "pgm.opts.redirect.res2", FT_UINT16, BASE_HEX,
1338           NULL, 0x0, "", HFILL }},
1339     { &hf_pgm_opt_redirect_dlr,
1340       { "DLR", "pgm.opts.redirect.dlr", FT_IPv4, BASE_NONE,
1341           NULL, 0x0, "", HFILL }},
1342     { &hf_pgm_opt_redirect_dlr6,
1343       { "DLR", "pgm.opts.redirect.dlr", FT_IPv6, BASE_NONE,
1344           NULL, 0x0, "", HFILL }},
1345     { &hf_pgm_opt_fragment_res,
1346       { "Reserved", "pgm.opts.fragment.res", FT_UINT8, BASE_HEX,
1347           NULL, 0x0, "", HFILL }},
1348     { &hf_pgm_opt_fragment_first_sqn,
1349       { "First Sequence Number", "pgm.opts.fragment.first_sqn", FT_UINT32, BASE_HEX,
1350           NULL, 0x0, "", HFILL }},
1351     { &hf_pgm_opt_fragment_offset,
1352       { "Fragment Offset", "pgm.opts.fragment.fragment_offset", FT_UINT32, BASE_DEC,
1353           NULL, 0x0, "", HFILL }},
1354     { &hf_pgm_opt_fragment_total_length,
1355       { "Total Length", "pgm.opts.fragment.total_length", FT_UINT32, BASE_DEC,
1356           NULL, 0x0, "", HFILL }}
1357   };
1358   static gint *ett[] = {
1359         &ett_pgm,
1360         &ett_pgm_optbits,
1361         &ett_pgm_spm,
1362         &ett_pgm_data,
1363         &ett_pgm_nak,
1364         &ett_pgm_poll,
1365         &ett_pgm_polr,
1366         &ett_pgm_ack,
1367         &ett_pgm_opts,
1368         &ett_pgm_opts_join,
1369         &ett_pgm_opts_parityprm,
1370         &ett_pgm_opts_paritygrp,
1371         &ett_pgm_opts_naklist,
1372         &ett_pgm_opts_ccdata,
1373         &ett_pgm_opts_nak_bo_ivl,
1374         &ett_pgm_opts_nak_bo_rng,
1375         &ett_pgm_opts_redirect,
1376         &ett_pgm_opts_fragment
1377   };
1378   module_t *pgm_module;
1379
1380   proto_pgm = proto_register_protocol("Pragmatic General Multicast",
1381                                        "PGM", "pgm");
1382
1383   proto_register_field_array(proto_pgm, hf, array_length(hf));
1384   proto_register_subtree_array(ett, array_length(ett));
1385
1386         /* subdissector code */
1387   subdissector_table = register_dissector_table("pgm.port",
1388                 "PGM port", FT_UINT16, BASE_DEC);
1389   register_heur_dissector_list("pgm", &heur_subdissector_list);
1390
1391   /*
1392    * Register configuration preferences for UDP encapsulation
1393    * (Note: Initially the ports are set to zero and the ports
1394    *        are not registered so the dissecting of PGM 
1395    *        encapsulated in UDP packets is off by default;
1396    *        dissector_add_handle is called so that pgm
1397    *        is available for 'decode-as'
1398    */
1399   pgm_module = prefs_register_protocol(proto_pgm, proto_reg_handoff_pgm);
1400
1401   prefs_register_bool_preference(pgm_module, "check_checksum",
1402                 "Check the validity of the PGM checksum when possible",
1403                 "Whether to check the validity of the PGM checksum",
1404                 &pgm_check_checksum);
1405
1406   prefs_register_uint_preference(pgm_module, "udp.encap_ucast_port",
1407                 "PGM Encap Unicast Port (standard is 3055)",
1408                 "PGM Encap is PGM packets encapsulated in UDP packets"
1409                 " (Note: This option is off, i.e. port is 0, by default)",
1410                 10, &udp_encap_ucast_port);
1411
1412   prefs_register_uint_preference(pgm_module, "udp.encap_mcast_port",
1413                 "PGM Encap Multicast Port (standard is 3056)",
1414                 "PGM Encap is PGM packets encapsulated in UDP packets"
1415                 " (Note: This option is off, i.e. port is 0, by default)",
1416                 10, &udp_encap_mcast_port);
1417
1418 }
1419
1420 /* The registration hand-off routine */
1421 /*
1422  * Set up PGM Encap dissecting, which is off by default for UDP
1423  */
1424
1425 void
1426 proto_reg_handoff_pgm(void)
1427 {
1428   static gboolean initialized = FALSE;
1429   static dissector_handle_t pgm_handle;
1430   static guint old_udp_encap_ucast_port;
1431   static guint old_udp_encap_mcast_port;
1432
1433   if (! initialized) {
1434     pgm_handle = create_dissector_handle(dissect_pgm, proto_pgm);
1435     dissector_add_handle("udp.port", pgm_handle);  /* for 'decode-as' */
1436     dissector_add("ip.proto", IP_PROTO_PGM, pgm_handle);
1437     data_handle = find_dissector("data");
1438     initialized = TRUE;
1439   } else {
1440     if (old_udp_encap_ucast_port != 0) {
1441       dissector_delete("udp.port", old_udp_encap_ucast_port, pgm_handle);
1442     }
1443     if (old_udp_encap_mcast_port != 0) {
1444       dissector_delete("udp.port", old_udp_encap_mcast_port, pgm_handle);
1445     }
1446   }
1447
1448   if (udp_encap_ucast_port != 0) {
1449     dissector_add("udp.port", udp_encap_ucast_port, pgm_handle);
1450   }
1451   if (udp_encap_mcast_port != 0) {
1452     dissector_add("udp.port", udp_encap_mcast_port, pgm_handle);
1453   }
1454   old_udp_encap_ucast_port = udp_encap_ucast_port;
1455   old_udp_encap_mcast_port = udp_encap_mcast_port;
1456 }