quota update partial NTGetUserQuota support
[obnox/wireshark/wip.git] / packet-pgm.c
1 /* packet-pgm.c
2  * Routines for pgm packet disassembly
3  *
4  * $Id: packet-pgm.c,v 1.14 2002/01/21 07:36:38 guy Exp $
5  * 
6  * Copyright (c) 2000 by Talarian Corp
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1999 Gerald Combs
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #ifdef HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
37 #endif
38
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <ctype.h>
46 #include <time.h>
47 #include <string.h>
48 #include <epan/packet.h>
49 #include "packet-pgm.h"
50 #include "afn.h"
51 #include "ipproto.h"
52 #include <epan/resolv.h>
53 #include <epan/strutil.h>
54 #include <epan/conversation.h>
55 #include "prefs.h"
56
57 #include <epan/proto.h>
58
59 void proto_reg_handoff_pgm(void);
60 static void proto_rereg_pgm(void);
61
62 static int udp_encap_ucast_port = 0;
63 static int udp_encap_mcast_port = 0;
64 static int old_encap_ucast_port = 0;
65 static int old_encap_mcast_port = 0;
66
67 static int proto_pgm = -1;
68 static int ett_pgm = -1;
69 static int ett_pgm_optbits = -1;
70 static int ett_pgm_opts = -1;
71 static int ett_pgm_spm = -1;
72 static int ett_pgm_data = -1;
73 static int ett_pgm_nak = -1;
74 static int ett_pgm_ack = -1;
75 static int ett_pgm_opts_join = -1;
76 static int ett_pgm_opts_parityprm = -1;
77 static int ett_pgm_opts_paritygrp = -1;
78 static int ett_pgm_opts_naklist = -1;
79 static int ett_pgm_opts_ccdata = -1;
80
81 static int hf_pgm_main_sport = -1;
82 static int hf_pgm_main_dport = -1;
83 static int hf_pgm_main_type = -1;
84 static int hf_pgm_main_opts = -1;
85 static int hf_pgm_main_opts_opt = -1;
86 static int hf_pgm_main_opts_netsig = -1;
87 static int hf_pgm_main_opts_varlen = -1;
88 static int hf_pgm_main_opts_parity = -1;
89 static int hf_pgm_main_cksum = -1;
90 static int hf_pgm_main_gsi = -1;
91 static int hf_pgm_main_tsdulen = -1;
92 static int hf_pgm_spm_sqn = -1;
93 static int hf_pgm_spm_lead = -1;
94 static int hf_pgm_spm_trail = -1;
95 static int hf_pgm_spm_pathafi = -1;
96 static int hf_pgm_spm_res = -1;
97 static int hf_pgm_spm_path = -1;
98 static int hf_pgm_data_sqn = -1;
99 static int hf_pgm_data_trail = -1;
100 static int hf_pgm_nak_sqn = -1;
101 static int hf_pgm_nak_srcafi = -1;
102 static int hf_pgm_nak_srcres = -1;
103 static int hf_pgm_nak_src = -1;
104 static int hf_pgm_nak_grpafi = -1;
105 static int hf_pgm_nak_grpres = -1;
106 static int hf_pgm_nak_grp = -1;
107 static int hf_pgm_ack_sqn = -1;
108 static int hf_pgm_ack_bitmap = -1;
109
110 static int hf_pgm_opt_type = -1;
111 static int hf_pgm_opt_len = -1;
112 static int hf_pgm_opt_tlen = -1;
113
114 static int hf_pgm_genopt_type = -1;
115 static int hf_pgm_genopt_len = -1;
116 static int hf_pgm_genopt_opx = -1;
117
118 static int hf_pgm_opt_join_res = -1;
119 static int hf_pgm_opt_join_minjoin = -1;
120
121 static int hf_pgm_opt_parity_prm_po = -1;
122 static int hf_pgm_opt_parity_prm_prmtgsz = -1;
123
124 static int hf_pgm_opt_parity_grp_res = -1;
125 static int hf_pgm_opt_parity_grp_prmgrp = -1;
126
127 static int hf_pgm_opt_curr_tgsize_type = -1;
128 static int hf_pgm_opt_curr_tgsize_len = -1;
129 static int hf_pgm_opt_curr_tgsize_opx = -1;
130 static int hf_pgm_opt_curr_tgsize_res = -1;
131 static int hf_pgm_opt_curr_tgsize_prmatgsz = -1;
132
133 static int hf_pgm_opt_nak_res = -1;
134 static int hf_pgm_opt_nak_list = -1;
135
136 static int hf_pgm_opt_ccdata_res = -1;
137 static int hf_pgm_opt_ccdata_tsp = -1;
138 static int hf_pgm_opt_ccdata_afi = -1;
139 static int hf_pgm_opt_ccdata_res2 = -1;
140 static int hf_pgm_opt_ccdata_acker = -1;
141
142 static int hf_pgm_opt_ccfeedbk_res = -1;
143 static int hf_pgm_opt_ccfeedbk_tsp = -1;
144 static int hf_pgm_opt_ccfeedbk_afi = -1;
145 static int hf_pgm_opt_ccfeedbk_lossrate = -1;
146 static int hf_pgm_opt_ccfeedbk_acker = -1;
147
148 static dissector_table_t subdissector_table;
149 static heur_dissector_list_t heur_subdissector_list;
150 static dissector_handle_t data_handle;
151
152 /*
153  * As of the time this comment was typed
154  *
155  *      http://search.ietf.org/internet-drafts/draft-speakman-pgm-spec-06.txt
156  *
157  * was the URL for the PGM draft.
158  */
159
160 static char *
161 optsstr(nchar_t opts)
162 {
163         static char msg[256];
164         char *p = msg, *str;
165
166         if (opts == 0)
167                 return("");
168
169         if (opts & PGM_OPT){
170                 sprintf(p, "Present");
171                 p += strlen("Present");
172         }
173         if (opts & PGM_OPT_NETSIG){
174                 if (p != msg)
175                         str = ",NetSig";
176                 else
177                         str = "NetSig";
178                 sprintf(p, str);
179                 p += strlen(str);
180         }
181         if (opts & PGM_OPT_VAR_PKTLEN){
182                 if (p != msg)
183                         str = ",VarLen";
184                 else
185                         str = "VarLen";
186                 sprintf(p, str);
187                 p += strlen(str);
188         }
189         if (opts & PGM_OPT_PARITY){
190                 if (p != msg)
191                         str = ",Parity";
192                 else
193                         str = "Parity";
194                 sprintf(p, str);
195                 p += strlen(str);
196         }
197         if (p == msg) {
198                 sprintf(p, "0x%x", opts);
199         }
200         return(msg);
201 }
202 static char *
203 paritystr(nchar_t parity)
204 {
205         static char msg[256];
206         char *p = msg, *str;
207
208         if (parity == 0)
209                 return("");
210
211         if (parity & PGM_OPT_PARITY_PRM_PRO){
212                 sprintf(p, "Pro-active");
213                 p += strlen("Pro-active");
214         }
215         if (parity & PGM_OPT_PARITY_PRM_OND){
216                 if (p != msg)
217                         str = ",On-demand";
218                 else
219                         str = "On-demand";
220                 sprintf(p, str);
221                 p += strlen(str);
222         }
223         if (p == msg) {
224                 sprintf(p, "0x%x", parity);
225         }
226         return(msg);
227 }
228
229 static const value_string opt_vals[] = {
230         { PGM_OPT_LENGTH,      "Length" },
231         { PGM_OPT_END,         "End" },
232         { PGM_OPT_FRAGMENT,    "Fragment" },
233         { PGM_OPT_NAK_LIST,    "NakList" },
234         { PGM_OPT_JOIN,        "Join" },
235         { PGM_OPT_REDIRECT,    "ReDirect" },
236         { PGM_OPT_SYN,         "Syn" },
237         { PGM_OPT_FIN,         "Fin" },
238         { PGM_OPT_RST,         "Rst" },
239         { PGM_OPT_PARITY_PRM,  "ParityPrm" },
240         { PGM_OPT_PARITY_GRP,  "ParityGrp" },
241         { PGM_OPT_CURR_TGSIZE, "CurrTgsiz" },
242         { PGM_OPT_PGMCC_DATA,  "CcData" },
243         { PGM_OPT_PGMCC_FEEDBACK, "CcFeedBack" },
244         { 0,                   NULL }
245 };
246
247 static const value_string opx_vals[] = {
248         { PGM_OPX_IGNORE,  "Ignore" },
249         { PGM_OPX_INVAL,   "Inval" },
250         { PGM_OPX_DISCARD, "DisCard" },
251         { 0,               NULL }
252 };
253
254 static void
255 dissect_pgmopts(tvbuff_t *tvb, int offset, proto_tree *tree,
256     const char *pktname)
257 {
258         proto_item *tf;
259         proto_tree *opts_tree = NULL;
260         proto_tree *opt_tree = NULL;
261         pgm_opt_length_t opts;
262         pgm_opt_generic_t genopts;
263         int theend = 0, firsttime = 1;
264
265         tvb_memcpy(tvb, (guint8 *)&opts, offset, sizeof(opts));
266         opts.total_len = ntohs(opts.total_len);
267
268         tf = proto_tree_add_text(tree, tvb, offset, 
269                 opts.total_len, 
270                 "%s Options (Total Length %d)", pktname, opts.total_len);
271         opts_tree = proto_item_add_subtree(tf, ett_pgm_opts);
272         proto_tree_add_uint(opts_tree, hf_pgm_opt_type, tvb, 
273                 offset, 1, opts.type);
274         proto_tree_add_uint(opts_tree, hf_pgm_opt_len, tvb, 
275                 offset+1, 1, opts.len);
276         proto_tree_add_uint(opts_tree, hf_pgm_opt_tlen, tvb, 
277                 offset+2, 2, opts.total_len);
278
279         offset += 4;
280         for (opts.total_len -= 4; opts.total_len > 0;){
281                 tvb_memcpy(tvb, (guint8 *)&genopts, offset, sizeof(genopts));
282                 if (genopts.type & PGM_OPT_END)  {
283                         genopts.type &= ~PGM_OPT_END;
284                         theend = 1;
285                 }
286                 tf = proto_tree_add_text(opts_tree, tvb, offset, genopts.len,
287                         "Option: %s, Length: %u",
288                         val_to_str(genopts.type, opt_vals, "Unknown (0x%02x)"),
289                         genopts.len);
290                 if (genopts.len == 0)
291                         break;
292
293                 switch(genopts.type) {
294                 case PGM_OPT_JOIN:{
295                         pgm_opt_join_t optdata;
296
297                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
298                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_join);
299
300                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_type, 
301                                 tvb, offset, 1, genopts.type);
302
303                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb, 
304                                 offset+1, 1, genopts.len);
305
306                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx, tvb, 
307                                 offset+2, 1, genopts.opx);
308
309                         proto_tree_add_uint(opt_tree, hf_pgm_opt_join_res, tvb, 
310                                 offset+3, 1, optdata.res);
311
312                         proto_tree_add_uint(opt_tree, hf_pgm_opt_join_minjoin, tvb, 
313                                 offset+4, 4, ntohl(optdata.opt_join_min));
314
315                         break;
316                 }
317                 case PGM_OPT_PARITY_PRM:{
318                         pgm_opt_parity_prm_t optdata;
319
320                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
321                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_parityprm);
322
323                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_type, 
324                                 tvb, offset, 1, genopts.type);
325
326                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb, 
327                                 offset+1, 1, genopts.len);
328
329                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx, 
330                                 tvb, offset+2, 1, genopts.opx);
331
332                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_parity_prm_po, tvb, 
333                                 offset+3, 1, optdata.po, "Parity Parameters: %s (0x%x)",
334                                 paritystr(optdata.po), optdata.po);
335
336                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_prm_prmtgsz,
337                                 tvb, offset+4, 4, ntohl(optdata.prm_tgsz));
338
339                         break;
340                 }
341                 case PGM_OPT_PARITY_GRP:{
342                         pgm_opt_parity_grp_t optdata;
343
344                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
345                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_paritygrp);
346
347                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_type, 
348                                 tvb, offset, 1, genopts.type);
349
350                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb, 
351                                 offset+1, 1, genopts.len);
352
353                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx, 
354                                 tvb, offset+2, 1, genopts.opx);
355
356                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_grp_res, tvb, 
357                                 offset+3, 1, optdata.res);
358
359                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_grp_prmgrp,
360                                 tvb, offset+4, 4, ntohl(optdata.prm_grp));
361
362                         break;
363                 }
364                 case PGM_OPT_NAK_LIST:{
365                         pgm_opt_nak_list_t optdata;
366                         nlong_t naklist[PGM_MAX_NAK_LIST_SZ+1];
367                         char nakbuf[8192], *ptr;
368                         int i, j, naks, soffset = 0;
369
370                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
371                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_naklist);
372
373                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_type, tvb, 
374                                 offset, 1, genopts.type);
375
376                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb, 
377                                 offset+1, 1, genopts.len);
378
379                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx, 
380                                 tvb, offset+2, 1, genopts.opx);
381
382                         proto_tree_add_uint(opt_tree, hf_pgm_opt_nak_res, tvb, 
383                                 offset+3, 1, optdata.res);
384
385                         optdata.len -= sizeof(pgm_opt_nak_list_t);
386                         tvb_memcpy(tvb, (guint8 *)naklist, offset+4, optdata.len);
387                         naks = (optdata.len/sizeof(nlong_t));
388                         ptr = nakbuf;
389                         j = 0;
390                         /*
391                          * Print out 8 per line 
392                          */
393                         for (i=0; i < naks; i++) {
394                                 sprintf(nakbuf+soffset, "0x%lx ",
395                                     (unsigned long)ntohl(naklist[i]));
396                                 soffset = strlen(nakbuf);
397                                 if ((++j % 8) == 0) {
398                                         if (firsttime) {
399                                                 proto_tree_add_bytes_format(opt_tree, 
400                                                         hf_pgm_opt_nak_list, tvb, offset+4, optdata.len,
401                                                         nakbuf, "List(%d): %s", naks, nakbuf);
402                                                         soffset = 0;
403                                         } else {
404                                                 proto_tree_add_bytes_format(opt_tree, 
405                                                         hf_pgm_opt_nak_list, tvb, offset+4, optdata.len, 
406                                                         nakbuf, "List: %s", nakbuf);
407                                                         soffset = 0;
408                                         }
409                                         firsttime = 0;
410                                 }
411                         }
412                         if (soffset) {
413                                 if (firsttime) {
414                                         proto_tree_add_bytes_format(opt_tree, 
415                                                 hf_pgm_opt_nak_list, tvb, offset+4, optdata.len,
416                                                 nakbuf, "List(%d): %s", naks, nakbuf);
417                                                 soffset = 0;
418                                 } else {
419                                         proto_tree_add_bytes_format(opt_tree, 
420                                                 hf_pgm_opt_nak_list, tvb, offset+4, optdata.len, 
421                                                 nakbuf, "List: %s", nakbuf);
422                                                 soffset = 0;
423                                 }
424                         }
425                         break;
426                 }
427                 case PGM_OPT_PGMCC_DATA:{
428                         pgm_opt_pgmcc_data_t optdata;
429
430                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
431                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_ccdata);
432
433                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_type, 
434                                 tvb, offset, 1, genopts.type);
435
436                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb, 
437                                 offset+1, 1, genopts.len);
438
439                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx, 
440                                 tvb, offset+2, 1, genopts.opx);
441
442                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_res, tvb, 
443                                 offset+3, 1, optdata.res);
444
445                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_tsp, tvb, 
446                                 offset+4, 4, optdata.tsp);
447
448                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_afi, tvb, 
449                                 offset+8, 2, ntohs(optdata.acker_afi));
450
451                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_res2, tvb, 
452                                 offset+10, 2, ntohs(optdata.res2));
453
454                         switch (ntohs(optdata.acker_afi)) {
455
456                         case AFNUM_INET:
457                                 proto_tree_add_ipv4(opt_tree, hf_pgm_opt_ccdata_acker,
458                                     tvb, offset+12, 4, optdata.acker);
459                                 break;
460
461                         default:
462                                 /*
463                                  * XXX - the header is variable-length,
464                                  * as the length of the NLA depends on
465                                  * its AFI.
466                                  *
467                                  * However, our structure for it is
468                                  * fixed-length, and assumes it's a 4-byte
469                                  * IPv4 address.
470                                  */
471                                 break;
472                         }
473
474                         break;
475                 }
476                 case PGM_OPT_PGMCC_FEEDBACK:{
477                         pgm_opt_pgmcc_feedback_t optdata;
478
479                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
480                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_ccdata);
481
482                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_type, 
483                                 tvb, offset, 1, genopts.type);
484
485                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb, 
486                                 offset+1, 1, genopts.len);
487
488                         proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx, 
489                                 tvb, offset+2, 1, genopts.opx);
490
491                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_res, tvb, 
492                                 offset+3, 1, optdata.res);
493
494                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_tsp, tvb, 
495                                 offset+4, 4, optdata.tsp);
496
497                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_afi, tvb, 
498                                 offset+8, 2, ntohs(optdata.acker_afi));
499
500                         proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_lossrate, tvb, 
501                                 offset+10, 2, ntohs(optdata.loss_rate));
502
503                         switch (ntohs(optdata.acker_afi)) {
504
505                         case AFNUM_INET:
506                                 proto_tree_add_ipv4(opt_tree, hf_pgm_opt_ccfeedbk_acker,
507                                     tvb, offset+12, 4, optdata.acker);
508                                 break;
509
510                         default:
511                                 /*
512                                  * XXX - the header is variable-length,
513                                  * as the length of the NLA depends on
514                                  * its AFI.
515                                  *
516                                  * However, our structure for it is
517                                  * fixed-length, and assumes it's a 4-byte
518                                  * IPv4 address.
519                                  */
520                                 break;
521                         }
522
523                         break;
524                 }
525                 }
526                 offset += genopts.len;
527                 opts.total_len -= genopts.len;
528
529         }
530         return ;
531 }
532
533 static const value_string type_vals[] = {
534         { PGM_SPM_PCKT,   "SPM" },
535         { PGM_RDATA_PCKT, "RDATA" },
536         { PGM_ODATA_PCKT, "ODATA" },
537         { PGM_NAK_PCKT,   "NAK" },
538         { PGM_NNAK_PCKT,  "NNAK" },
539         { PGM_NCF_PCKT,   "NCF" },
540         { PGM_ACK_PCKT,   "ACK" },
541         { 0,              NULL }
542 };
543 /* Determine if there is a sub-dissector and call it.  This has been */
544 /* separated into a stand alone routine to other protocol dissectors */
545 /* can call to it, ie. socks    */
546
547 void
548 decode_pgm_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
549         proto_tree *tree, pgm_type *pgmhdr)
550 {
551   tvbuff_t *next_tvb;
552   int found = 0;
553
554   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
555
556   /* do lookup with the subdissector table */
557   found = dissector_try_port(subdissector_table, pgmhdr->sport, 
558                         next_tvb, pinfo, tree);
559   if (found)
560         return;
561
562   found = dissector_try_port(subdissector_table, pgmhdr->dport, 
563                         next_tvb, pinfo, tree);
564   if (found)
565         return;
566
567   /* do lookup with the heuristic subdissector table */
568   if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
569     return;
570
571   /* Oh, well, we don't know this; dissect it as data. */
572   call_dissector(data_handle,next_tvb, pinfo, tree);
573
574 }
575 int 
576 total_size(tvbuff_t *tvb, pgm_type *hdr)
577 {
578         int bytes = sizeof(pgm_type);
579         pgm_opt_length_t opts;
580
581         switch(hdr->type) {
582         case PGM_SPM_PCKT:
583                 bytes += sizeof(pgm_spm_t);
584                 break;
585
586         case PGM_RDATA_PCKT:
587         case PGM_ODATA_PCKT:
588                 bytes += sizeof(pgm_data_t);
589                 break;
590
591         case PGM_NAK_PCKT:
592         case PGM_NNAK_PCKT:
593         case PGM_NCF_PCKT:
594                 bytes += sizeof(pgm_nak_t);
595                 break;
596         case PGM_ACK_PCKT:
597                 bytes += sizeof(pgm_ack_t);
598                 break;
599         }
600         if ((hdr->opts & PGM_OPT)) {
601                 tvb_memcpy(tvb, (guint8 *)&opts, bytes, sizeof(opts));
602                 bytes += ntohs(opts.total_len);
603         }
604         return(bytes);
605 }
606 /*
607  * dissect_pgm - The dissector for Pragmatic General Multicast
608  */
609 static void
610 dissect_pgm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
611 {
612         proto_tree *pgm_tree = NULL;
613         proto_tree *opt_tree = NULL;
614         proto_tree *type_tree = NULL;
615         proto_item *tf;
616         pgm_type pgmhdr;
617         pgm_spm_t spm;
618         pgm_data_t data;
619         pgm_nak_t nak;
620         pgm_ack_t ack;
621         int offset = 0;
622         guint hlen, plen;
623         proto_item *ti;
624         const char *pktname;
625         char *gsi;
626         int isdata = 0;
627
628         if (check_col(pinfo->cinfo, COL_PROTOCOL))
629                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "PGM");
630
631         /* Clear out the Info column. */
632         if (check_col(pinfo->cinfo, COL_INFO))
633                 col_clear(pinfo->cinfo, COL_INFO);
634
635         tvb_memcpy(tvb, (guint8 *)&pgmhdr, offset, sizeof(pgm_type));
636         hlen = sizeof(pgm_type);
637         pgmhdr.sport = ntohs(pgmhdr.sport);
638         pgmhdr.dport = ntohs(pgmhdr.dport);
639         pgmhdr.tsdulen = ntohs(pgmhdr.tsdulen);
640
641         pktname = val_to_str(pgmhdr.type, type_vals, "Unknown (0x%02x)");
642
643         gsi = bytes_to_str(pgmhdr.gsi, 6);
644         switch(pgmhdr.type) {
645         case PGM_SPM_PCKT:
646                 plen = sizeof(pgm_spm_t);
647                 tvb_memcpy(tvb, (guint8 *)&spm, sizeof(pgm_type), plen);
648                 spm_ntoh(&spm);
649                 if (check_col(pinfo->cinfo, COL_INFO)) {
650                         col_add_fstr(pinfo->cinfo, COL_INFO,
651                                 "%-5s sqn 0x%x gsi %s", pktname, spm.sqn, gsi);
652                 }
653                 break;
654
655         case PGM_RDATA_PCKT:
656         case PGM_ODATA_PCKT:
657                 plen = sizeof(pgm_data_t);
658                 tvb_memcpy(tvb, (guint8 *)&data, sizeof(pgm_type), plen);
659                 data_ntoh(&data);
660                 if (check_col(pinfo->cinfo, COL_INFO)) {
661                         col_add_fstr(pinfo->cinfo, COL_INFO,
662                             "%-5s sqn 0x%x gsi %s tsdulen %d", pktname, data.sqn, gsi,
663                             pgmhdr.tsdulen);
664                 }
665                 isdata = 1;
666                 break;
667
668         case PGM_NAK_PCKT:
669         case PGM_NNAK_PCKT:
670         case PGM_NCF_PCKT:
671                 plen = sizeof(pgm_nak_t);
672                 tvb_memcpy(tvb, (guint8 *)&nak, sizeof(pgm_type), plen);
673                 nak_ntoh(&nak);
674                 if (check_col(pinfo->cinfo, COL_INFO)) {
675                         col_add_fstr(pinfo->cinfo, COL_INFO,
676                                 "%-5s sqn 0x%x gsi %s", pktname, nak.sqn, gsi);
677                 }
678                 break;
679         case PGM_ACK_PCKT:
680                 plen = sizeof(pgm_ack_t);
681                 tvb_memcpy(tvb, (guint8 *)&ack, sizeof(pgm_type), plen);
682                 ack_ntoh(&ack);
683                 if (check_col(pinfo->cinfo, COL_INFO)) {
684                         col_add_fstr(pinfo->cinfo, COL_INFO,
685                             "%-5s sqn 0x%x gsi %s", pktname, ack.rx_max_sqn, gsi);
686                 }
687                 break;
688
689         default:
690                 return;
691         }
692
693         if (tree) {
694                 ti = proto_tree_add_protocol_format(tree, proto_pgm, 
695                         tvb, offset, total_size(tvb, &pgmhdr),
696                         "Pragmatic General Multicast: Type %s"
697                             " SrcPort %u, DstPort %u, GSI %s", pktname,
698                         pgmhdr.sport, pgmhdr.dport,
699                         bytes_to_str(pgmhdr.gsi, 6));
700
701                 pgm_tree = proto_item_add_subtree(ti, ett_pgm);
702                 proto_tree_add_uint(pgm_tree, hf_pgm_main_sport, tvb, offset, 2,
703                         pgmhdr.sport);
704                 proto_tree_add_uint(pgm_tree, hf_pgm_main_dport, tvb, offset+2, 
705                         2, pgmhdr.dport);
706                 proto_tree_add_uint(pgm_tree, hf_pgm_main_type, tvb, 
707                         offset+4, 1, pgmhdr.type);
708
709                 tf = proto_tree_add_uint_format(pgm_tree, hf_pgm_main_opts, tvb, 
710                         offset+5, 1, pgmhdr.opts, "Options: %s (0x%x)", 
711                         optsstr(pgmhdr.opts), pgmhdr.opts);
712                 opt_tree = proto_item_add_subtree(tf, ett_pgm_optbits);
713
714                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_opt, tvb, 
715                         offset+5, 1, (pgmhdr.opts & PGM_OPT));
716                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_netsig, tvb, 
717                         offset+5, 1, (pgmhdr.opts & PGM_OPT_NETSIG));
718                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_varlen, tvb, 
719                         offset+5, 1, (pgmhdr.opts & PGM_OPT_VAR_PKTLEN));
720                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_parity, tvb, 
721                         offset+5, 1, (pgmhdr.opts & PGM_OPT_PARITY));
722
723                 proto_tree_add_uint(pgm_tree, hf_pgm_main_cksum, tvb, offset+6, 
724                         2, pgmhdr.cksum);
725                 proto_tree_add_bytes(pgm_tree, hf_pgm_main_gsi, tvb, offset+8, 
726                         6, pgmhdr.gsi);
727                 proto_tree_add_uint(pgm_tree, hf_pgm_main_tsdulen, tvb, 
728                         offset+14, 2, pgmhdr.tsdulen);
729
730                 offset = sizeof(pgm_type);
731                 tf = proto_tree_add_text(pgm_tree, tvb, offset, plen, "%s Packet",
732                         pktname);
733                 switch(pgmhdr.type) {
734                 case PGM_SPM_PCKT:
735                         type_tree = proto_item_add_subtree(tf, ett_pgm_spm);
736
737                         proto_tree_add_uint(type_tree, hf_pgm_spm_sqn, tvb, 
738                                 offset, 4, spm.sqn);
739                         proto_tree_add_uint(type_tree, hf_pgm_spm_trail, tvb, 
740                                 offset+4, 4, spm.trail);
741                         proto_tree_add_uint(type_tree, hf_pgm_spm_lead, tvb, 
742                                 offset+8, 4, spm.lead);
743                         proto_tree_add_uint(type_tree, hf_pgm_spm_pathafi, tvb, 
744                                 offset+10, 2, spm.path_afi);
745                         proto_tree_add_uint(type_tree, hf_pgm_spm_res, tvb, 
746                                 offset+12, 2, spm.res);
747                         switch (spm.path_afi) {
748
749                         case AFNUM_INET:
750                                 proto_tree_add_ipv4(type_tree, hf_pgm_spm_path,
751                                     tvb, offset+14, 4, spm.path);
752                                 break;
753
754                         default:
755                                 /*
756                                  * XXX - the header is variable-length,
757                                  * as the length of the NLA depends on
758                                  * its AFI.
759                                  *
760                                  * However, our structure for it is
761                                  * fixed-length, and assumes it's a 4-byte
762                                  * IPv4 address.
763                                  */
764                                 return;
765                         }
766
767                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
768                                 break;
769                         offset += plen;
770
771                         dissect_pgmopts(tvb, offset, type_tree, pktname);
772
773                         break;
774
775                 case PGM_RDATA_PCKT:
776                 case PGM_ODATA_PCKT: {
777                         type_tree = proto_item_add_subtree(tf, ett_pgm_data);
778
779                         proto_tree_add_uint(type_tree, hf_pgm_spm_sqn, tvb, 
780                                 offset, 4, data.sqn);
781                         proto_tree_add_uint(type_tree, hf_pgm_spm_trail, tvb, 
782                                 offset+4, 4, data.trail);
783
784                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
785                                 break;
786                         offset += plen;
787
788                         dissect_pgmopts(tvb, offset, type_tree, pktname);
789
790                         break;
791                 }
792
793
794                 case PGM_NAK_PCKT:
795                 case PGM_NNAK_PCKT:
796                 case PGM_NCF_PCKT:
797                         type_tree = proto_item_add_subtree(tf, ett_pgm_nak);
798
799                         proto_tree_add_uint(type_tree, hf_pgm_nak_sqn, tvb, 
800                                 offset, 4, nak.sqn);
801                         proto_tree_add_uint(type_tree, hf_pgm_nak_srcafi, tvb, 
802                                 offset+4, 2, nak.src_afi);
803                         proto_tree_add_uint(type_tree, hf_pgm_nak_srcres, tvb, 
804                                 offset+6, 2, nak.src_res);
805
806                         switch (nak.src_afi) {
807
808                         case AFNUM_INET:
809                                 proto_tree_add_ipv4(type_tree, hf_pgm_nak_src,
810                                     tvb, offset+8, 4, nak.src);
811                                 break;
812
813                         default:
814                                 /*
815                                  * XXX - the header is variable-length,
816                                  * as the length of the NLA depends on
817                                  * its AFI.
818                                  *
819                                  * However, our structure for it is
820                                  * fixed-length, and assumes it's a 4-byte
821                                  * IPv4 address.
822                                  */
823                                 break;
824                         }
825
826                         proto_tree_add_uint(type_tree, hf_pgm_nak_grpafi, tvb, 
827                                 offset+12, 2, nak.grp_afi);
828                         proto_tree_add_uint(type_tree, hf_pgm_nak_grpres, tvb, 
829                                 offset+14, 2, nak.grp_res);
830
831                         switch (nak.grp_afi) {
832
833                         case AFNUM_INET:
834                                 proto_tree_add_ipv4(type_tree, hf_pgm_nak_grp,
835                                     tvb, offset+16, 4, nak.grp);
836                                 break;
837
838                         default:
839                                 /*
840                                  * XXX - the header is variable-length,
841                                  * as the length of the NLA depends on
842                                  * its AFI.
843                                  *
844                                  * However, our structure for it is
845                                  * fixed-length, and assumes it's a 4-byte
846                                  * IPv4 address.
847                                  */
848                                 return;
849                         }
850
851                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
852                                 break;
853                         offset += plen;
854
855                         dissect_pgmopts(tvb, offset, type_tree, pktname);
856
857                         break;
858                 case PGM_ACK_PCKT:
859                         type_tree = proto_item_add_subtree(tf, ett_pgm_ack);
860
861                         proto_tree_add_uint(type_tree, hf_pgm_ack_sqn, tvb, 
862                                 offset, 4, ack.rx_max_sqn);
863                         proto_tree_add_uint(type_tree, hf_pgm_ack_bitmap, tvb, 
864                                 offset+4, 4, ack.bitmap);
865
866                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
867                                 break;
868                         offset += plen;
869
870                         dissect_pgmopts(tvb, offset, type_tree, pktname);
871
872                         break;
873                 }
874
875         }
876         if (isdata) {
877                 /*
878                  * Now see if there are any sub-dissectors, if so call them
879                  */
880                 offset = total_size(tvb, &pgmhdr);
881                 decode_pgm_ports(tvb, offset, pinfo, tree, &pgmhdr);
882         }
883         pktname = NULL;
884 }
885 static const true_false_string opts_present = {      
886         "Present",
887         "Not Present" 
888 };
889
890 /* Register all the bits needed with the filtering engine */
891 void 
892 proto_register_pgm(void)
893 {
894   static hf_register_info hf[] = {
895     { &hf_pgm_main_sport,
896       { "Source Port", "pgm.hdr.sport", FT_UINT16, BASE_DEC,
897           NULL, 0x0, "", HFILL }},
898     { &hf_pgm_main_dport,
899       { "Destination Port", "pgm.hdr.dport", FT_UINT16, BASE_DEC,
900           NULL, 0x0, "", HFILL }},
901     { &hf_pgm_main_type,
902       { "Type", "pgm.hdr.type", FT_UINT8, BASE_HEX,
903           VALS(type_vals), 0x0, "", HFILL }},
904     { &hf_pgm_main_opts,
905       { "Options", "pgm.hdr.opts", FT_UINT8, BASE_HEX,
906           NULL, 0x0, "", HFILL }},
907     { &hf_pgm_main_opts_opt,
908       { "Options", "pgm.hdr.opts.opt", FT_BOOLEAN, BASE_NONE,
909           TFS(&opts_present), PGM_OPT, "", HFILL }},
910     { &hf_pgm_main_opts_netsig,
911       { "Network Significant Options", "pgm.hdr.opts.netsig", 
912           FT_BOOLEAN, BASE_NONE,
913           TFS(&opts_present), PGM_OPT_NETSIG, "", HFILL }},
914     { &hf_pgm_main_opts_varlen,
915       { "Variable length Parity Packet Option", "pgm.hdr.opts.varlen", 
916           FT_BOOLEAN, BASE_NONE,
917           TFS(&opts_present), PGM_OPT_VAR_PKTLEN, "", HFILL }},
918     { &hf_pgm_main_opts_parity,
919       { "Parity", "pgm.hdr.opts.parity", FT_BOOLEAN, BASE_NONE,
920           TFS(&opts_present), PGM_OPT_PARITY, "", HFILL }},
921     { &hf_pgm_main_cksum,
922       { "Checksum", "pgm.hdr.cksum", FT_UINT16, BASE_HEX,
923         NULL, 0x0, "", HFILL }},
924     { &hf_pgm_main_gsi,
925       { "Global Source Identifier", "pgm.hdr.gsi", FT_BYTES, BASE_HEX, 
926           NULL, 0x0, "", HFILL }},
927     { &hf_pgm_main_tsdulen,
928       { "Transport Service Data Unit Length", "pgm.hdr.tsdulen", FT_UINT16, 
929           BASE_DEC, NULL, 0x0, "", HFILL }},
930     { &hf_pgm_spm_sqn,
931       { "Sequence number", "pgm.spm.sqn", FT_UINT32, BASE_HEX,
932           NULL, 0x0, "", HFILL }},
933     { &hf_pgm_spm_trail,
934       { "Trailing Edge Sequence Number", "pgm.spm.trail", FT_UINT32, BASE_HEX, 
935           NULL, 0x0, "", HFILL }},
936     { &hf_pgm_spm_lead,
937       { "Leading Edge Sequence Number", "pgm.spm.lead", FT_UINT32, BASE_HEX, 
938           NULL, 0x0, "", HFILL }},
939     { &hf_pgm_spm_pathafi,
940       { "Path NLA AFI", "pgm.spm.pathafi", FT_UINT16, BASE_DEC,
941           VALS(afn_vals), 0x0, "", HFILL }},
942     { &hf_pgm_spm_res,
943       { "Reserved", "pgm.spm.res", FT_UINT16, BASE_HEX,
944           NULL, 0x0, "", HFILL }},
945     { &hf_pgm_spm_path,
946       { "Path NLA", "pgm.spm.path", FT_IPv4, BASE_NONE,
947           NULL, 0x0, "", HFILL }},
948     { &hf_pgm_data_sqn,
949       { "Data Packet Sequence Number", "pgm.data.sqn", FT_UINT32, BASE_HEX, 
950           NULL, 0x0, "", HFILL }},
951     { &hf_pgm_data_trail,
952       { "Trailing Edge Sequence Number", "pgm.data.trail", FT_UINT32, BASE_HEX, 
953           NULL, 0x0, "", HFILL }},
954     { &hf_pgm_nak_sqn,
955       { "Requested Sequence Number", "pgm.nak.sqn", FT_UINT32, BASE_HEX, 
956           NULL, 0x0, "", HFILL }},
957     { &hf_pgm_nak_srcafi,
958       { "Source NLA AFI", "pgm.nak.srcafi", FT_UINT16, BASE_DEC,
959           VALS(afn_vals), 0x0, "", HFILL }},
960     { &hf_pgm_nak_srcres,
961       { "Reserved", "pgm.nak.srcres", FT_UINT16, BASE_HEX,
962           NULL, 0x0, "", HFILL }},
963     { &hf_pgm_nak_src,
964       { "Source NLA", "pgm.nak.src", FT_IPv4, BASE_NONE,
965           NULL, 0x0, "", HFILL }},
966     { &hf_pgm_nak_grpafi,
967       { "Multicast Group AFI", "pgm.nak.grpafi", FT_UINT16, BASE_DEC,
968           VALS(afn_vals), 0x0, "", HFILL }},
969     { &hf_pgm_nak_grpres,
970       { "Reserved", "pgm.nak.grpres", FT_UINT16, BASE_HEX,
971           NULL, 0x0, "", HFILL }},
972     { &hf_pgm_nak_grp,
973       { "Multicast Group NLA", "pgm.nak.grp", FT_IPv4, BASE_NONE,
974           NULL, 0x0, "", HFILL }},
975     { &hf_pgm_ack_sqn,
976       { "Maximum Received Sequence Number", "pgm.ack.maxsqn", FT_UINT32, 
977           BASE_HEX, NULL, 0x0, "", HFILL }},
978     { &hf_pgm_ack_bitmap,
979       { "Packet Bitmap", "pgm.ack.bitmap", FT_UINT32, BASE_HEX, 
980           NULL, 0x0, "", HFILL }},
981     { &hf_pgm_opt_type,
982       { "Type", "pgm.opts.type", FT_UINT8, BASE_HEX,
983           VALS(opt_vals), 0x0, "", HFILL }},
984     { &hf_pgm_opt_len,
985       { "Length", "pgm.opts.len", FT_UINT8, BASE_DEC,
986           NULL, 0x0, "", HFILL }},
987     { &hf_pgm_opt_tlen,
988       { "Total Length", "pgm.opts.tlen", FT_UINT16, BASE_DEC,
989           NULL, 0x0, "", HFILL }},
990     { &hf_pgm_genopt_type,
991       { "Type", "pgm.genopts.type", FT_UINT8, BASE_HEX,
992           VALS(opt_vals), 0x0, "", HFILL }},
993     { &hf_pgm_genopt_len,
994       { "Length", "pgm.genopts.len", FT_UINT8, BASE_DEC,
995           NULL, 0x0, "", HFILL }},
996     { &hf_pgm_genopt_opx,
997       { "Option Extensibility Bits", "pgm.genopts.opx", FT_UINT8, BASE_HEX,
998           VALS(opx_vals), 0x0, "", HFILL }},
999     { &hf_pgm_opt_parity_prm_po,
1000       { "Parity Parameters", "pgm.opts.parity_prm.op", FT_UINT8, BASE_HEX,
1001           NULL, 0x0, "", HFILL }},
1002     { &hf_pgm_opt_parity_prm_prmtgsz,
1003       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp",
1004           FT_UINT32, BASE_HEX,
1005           NULL, 0x0, "", HFILL }},
1006     { &hf_pgm_opt_join_res,
1007       { "Reserved", "pgm.opts.join.res", FT_UINT8, BASE_HEX,
1008           NULL, 0x0, "", HFILL }},
1009     { &hf_pgm_opt_join_minjoin,
1010       { "Minimum Sequence Number", "pgm.opts.join.min_join",
1011           FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1012     { &hf_pgm_opt_parity_grp_res,
1013       { "Reserved", "pgm.opts.parity_prm.op", FT_UINT8, BASE_HEX,
1014           NULL, 0x0, "", HFILL }},
1015     { &hf_pgm_opt_parity_grp_prmgrp,
1016       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp",
1017           FT_UINT32, BASE_HEX,
1018           NULL, 0x0, "", HFILL }},
1019     { &hf_pgm_opt_nak_res,
1020       { "Reserved", "pgm.opts.nak.op", FT_UINT8, BASE_HEX,
1021           NULL, 0x0, "", HFILL }},
1022     { &hf_pgm_opt_nak_list,
1023       { "List", "pgm.opts.nak.list", FT_BYTES, BASE_NONE,
1024           NULL, 0x0, "", HFILL }},
1025     { &hf_pgm_opt_ccdata_res,
1026       { "Reserved", "pgm.opts.ccdata.res", FT_UINT8, BASE_DEC,
1027           NULL, 0x0, "", HFILL }},
1028     { &hf_pgm_opt_ccdata_tsp,
1029       { "Time Stamp", "pgm.opts.ccdata.tstamp", FT_UINT16, BASE_HEX,
1030           NULL, 0x0, "", HFILL }},
1031     { &hf_pgm_opt_ccdata_afi,
1032       { "Acker AFI", "pgm.opts.ccdata.afi", FT_UINT16, BASE_DEC,
1033           VALS(afn_vals), 0x0, "", HFILL }},
1034     { &hf_pgm_opt_ccdata_res2,
1035       { "Reserved", "pgm.opts.ccdata.res2", FT_UINT16, BASE_DEC,
1036           NULL, 0x0, "", HFILL }},
1037     { &hf_pgm_opt_ccdata_acker,
1038       { "Acker", "pgm.opts.ccdata.acker", FT_IPv4, BASE_NONE,
1039           NULL, 0x0, "", HFILL }},
1040     { &hf_pgm_opt_ccfeedbk_res,
1041       { "Reserved", "pgm.opts.ccdata.res", FT_UINT8, BASE_DEC,
1042           NULL, 0x0, "", HFILL }},
1043     { &hf_pgm_opt_ccfeedbk_tsp,
1044       { "Time Stamp", "pgm.opts.ccdata.tstamp", FT_UINT16, BASE_HEX,
1045           NULL, 0x0, "", HFILL }},
1046     { &hf_pgm_opt_ccfeedbk_afi,
1047       { "Acker AFI", "pgm.opts.ccdata.afi", FT_UINT16, BASE_DEC,
1048           VALS(afn_vals), 0x0, "", HFILL }},
1049     { &hf_pgm_opt_ccfeedbk_lossrate,
1050       { "Loss Rate", "pgm.opts.ccdata.lossrate", FT_UINT16, BASE_HEX,
1051           NULL, 0x0, "", HFILL }},
1052     { &hf_pgm_opt_ccfeedbk_acker,
1053       { "Acker", "pgm.opts.ccdata.acker", FT_IPv4, BASE_NONE,
1054           NULL, 0x0, "", HFILL }},
1055   };
1056   static gint *ett[] = {
1057     &ett_pgm,
1058         &ett_pgm_optbits,
1059         &ett_pgm_spm,
1060         &ett_pgm_data,
1061         &ett_pgm_nak,
1062         &ett_pgm_ack,
1063         &ett_pgm_opts,
1064         &ett_pgm_opts_join,
1065         &ett_pgm_opts_parityprm,
1066         &ett_pgm_opts_paritygrp,
1067         &ett_pgm_opts_naklist,
1068         &ett_pgm_opts_ccdata,
1069   };
1070   module_t *pgm_module;
1071
1072   proto_pgm = proto_register_protocol("Pragmatic General Multicast",
1073                                        "PGM", "pgm");
1074
1075   proto_register_field_array(proto_pgm, hf, array_length(hf));
1076   proto_register_subtree_array(ett, array_length(ett));
1077
1078         /* subdissector code */
1079   subdissector_table = register_dissector_table("pgm.port",
1080                 "PGM port", FT_UINT16, BASE_DEC);
1081   register_heur_dissector_list("pgm", &heur_subdissector_list);
1082
1083   /*
1084    * Register configuration preferences for UDP encapsulation
1085    * (Note: Initially the ports are set to zero so the 
1086    *        dissecting of PGM encapsulated in UPD packets
1087    *        is off by default)
1088    */
1089    pgm_module = prefs_register_protocol(proto_pgm, proto_rereg_pgm);
1090
1091    prefs_register_uint_preference(pgm_module, "udp.encap_ucast_port",
1092                 "PGM Encap Unicast Port (standard is 3055)", 
1093                 "PGM Encap is PGM packets encapsulated in UDP packets"
1094                 " (Note: This option is off, i.e. port is 0, by default)", 
1095                 10, &udp_encap_ucast_port);
1096    old_encap_ucast_port = udp_encap_ucast_port;
1097
1098    prefs_register_uint_preference(pgm_module, "udp.encap_mcast_port",
1099                 "PGM Encap Multicast Port (standard is 3056)", 
1100                 "PGM Encap is PGM packets encapsulated in UDP packets"
1101                 " (Note: This option is off, i.e. port is 0, by default)", 
1102                 10, &udp_encap_mcast_port);
1103
1104    old_encap_mcast_port = udp_encap_mcast_port;
1105 }
1106
1107 static dissector_handle_t pgm_handle;
1108
1109 /* The registration hand-off routine */
1110 void
1111 proto_reg_handoff_pgm(void)
1112 {
1113   pgm_handle = create_dissector_handle(dissect_pgm, proto_pgm);
1114
1115   /*
1116    * Set up PGM Encap dissecting, which is off by default
1117    */
1118   dissector_add("udp.port", udp_encap_ucast_port, pgm_handle);
1119   dissector_add("udp.port", udp_encap_mcast_port, pgm_handle);
1120
1121   dissector_add("ip.proto", IP_PROTO_PGM, pgm_handle);
1122   
1123   data_handle = find_dissector("data");
1124 }
1125
1126 static void
1127 proto_rereg_pgm(void)
1128 {
1129         /*
1130          * Remove the old ones
1131          */
1132         dissector_delete("udp.port", old_encap_ucast_port, pgm_handle);
1133         dissector_delete("udp.port", old_encap_mcast_port, pgm_handle);
1134
1135         /*
1136          * Set the new ones
1137          */
1138         dissector_add("udp.port", udp_encap_ucast_port, pgm_handle);
1139         dissector_add("udp.port", udp_encap_mcast_port, pgm_handle);
1140 }