Start of a PCNFSD dissector as the RPC dissector 150001.
[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.7 2001/08/06 19:05:14 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 "packet.h"
49 #include "packet-pgm.h"
50 #include "afn.h"
51 #include "ipproto.h"
52 #include "resolv.h"
53 #include "strutil.h"
54 #include "conversation.h"
55 #include "prefs.h"
56
57 #include "proto.h"
58
59 void proto_reg_handoff_pgm(void);
60 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 = -1;
115 static int hf_pgm_genopt_type = -1;
116 static int hf_pgm_genopt_len = -1;
117 static int hf_pgm_genopt_opx = -1;
118
119 static int hf_pgm_opt_join_res = -1;
120 static int hf_pgm_opt_join_minjoin = -1;
121
122 static int hf_pgm_opt_parity_prm_po = -1;
123 static int hf_pgm_opt_parity_prm_prmtgsz = -1;
124
125 static int hf_pgm_opt_parity_grp_res = -1;
126 static int hf_pgm_opt_parity_grp_prmgrp = -1;
127
128 static int hf_pgm_opt_curr_tgsize_type = -1;
129 static int hf_pgm_opt_curr_tgsize_len = -1;
130 static int hf_pgm_opt_curr_tgsize_opx = -1;
131 static int hf_pgm_opt_curr_tgsize_res = -1;
132 static int hf_pgm_opt_curr_tgsize_prmatgsz = -1;
133
134 static int hf_pgm_opt_nak_res = -1;
135 static int hf_pgm_opt_nak_list = -1;
136
137 static int hf_pgm_opt_ccdata_res = -1;
138 static int hf_pgm_opt_ccdata_tsp = -1;
139 static int hf_pgm_opt_ccdata_afi = -1;
140 static int hf_pgm_opt_ccdata_res2 = -1;
141 static int hf_pgm_opt_ccdata_acker = -1;
142
143 static int hf_pgm_opt_ccfeedbk_res = -1;
144 static int hf_pgm_opt_ccfeedbk_tsp = -1;
145 static int hf_pgm_opt_ccfeedbk_afi = -1;
146 static int hf_pgm_opt_ccfeedbk_lossrate = -1;
147 static int hf_pgm_opt_ccfeedbk_acker = -1;
148
149 static dissector_table_t subdissector_table;
150 static heur_dissector_list_t heur_subdissector_list;
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   dissect_data(next_tvb, 0, 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->fd, COL_PROTOCOL))
629                 col_set_str(pinfo->fd, COL_PROTOCOL, "PGM");
630
631         /* Clear out the Info column. */
632         if (check_col(pinfo->fd, COL_INFO))
633                 col_clear(pinfo->fd, 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->fd, COL_INFO)) {
650                         col_add_fstr(pinfo->fd, 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->fd, COL_INFO)) {
661                         col_add_fstr(pinfo->fd, 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->fd, COL_INFO)) {
675                         col_add_fstr(pinfo->fd, 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->fd, COL_INFO)) {
684                         col_add_fstr(pinfo->fd, 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                         tvbuff_t *next_tvb;
778
779                         type_tree = proto_item_add_subtree(tf, ett_pgm_data);
780
781                         proto_tree_add_uint(type_tree, hf_pgm_spm_sqn, tvb, 
782                                 offset, 4, data.sqn);
783                         proto_tree_add_uint(type_tree, hf_pgm_spm_trail, tvb, 
784                                 offset+4, 4, data.trail);
785
786                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
787                                 break;
788                         offset += plen;
789
790                         dissect_pgmopts(tvb, offset, type_tree, pktname);
791
792                         break;
793                 }
794
795
796                 case PGM_NAK_PCKT:
797                 case PGM_NNAK_PCKT:
798                 case PGM_NCF_PCKT:
799                         type_tree = proto_item_add_subtree(tf, ett_pgm_nak);
800
801                         proto_tree_add_uint(type_tree, hf_pgm_nak_sqn, tvb, 
802                                 offset, 4, nak.sqn);
803                         proto_tree_add_uint(type_tree, hf_pgm_nak_srcafi, tvb, 
804                                 offset+4, 2, nak.src_afi);
805                         proto_tree_add_uint(type_tree, hf_pgm_nak_srcres, tvb, 
806                                 offset+6, 2, nak.src_res);
807
808                         switch (nak.src_afi) {
809
810                         case AFNUM_INET:
811                                 proto_tree_add_ipv4(type_tree, hf_pgm_nak_src,
812                                     tvb, offset+8, 4, nak.src);
813                                 break;
814
815                         default:
816                                 /*
817                                  * XXX - the header is variable-length,
818                                  * as the length of the NLA depends on
819                                  * its AFI.
820                                  *
821                                  * However, our structure for it is
822                                  * fixed-length, and assumes it's a 4-byte
823                                  * IPv4 address.
824                                  */
825                                 break;
826                         }
827
828                         proto_tree_add_uint(type_tree, hf_pgm_nak_grpafi, tvb, 
829                                 offset+12, 2, nak.grp_afi);
830                         proto_tree_add_uint(type_tree, hf_pgm_nak_grpres, tvb, 
831                                 offset+14, 2, nak.grp_res);
832
833                         switch (nak.grp_afi) {
834
835                         case AFNUM_INET:
836                                 proto_tree_add_ipv4(type_tree, hf_pgm_nak_grp,
837                                     tvb, offset+16, 4, nak.grp);
838                                 break;
839
840                         default:
841                                 /*
842                                  * XXX - the header is variable-length,
843                                  * as the length of the NLA depends on
844                                  * its AFI.
845                                  *
846                                  * However, our structure for it is
847                                  * fixed-length, and assumes it's a 4-byte
848                                  * IPv4 address.
849                                  */
850                                 return;
851                         }
852
853                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
854                                 break;
855                         offset += plen;
856
857                         dissect_pgmopts(tvb, offset, type_tree, pktname);
858
859                         break;
860                 case PGM_ACK_PCKT:
861                         type_tree = proto_item_add_subtree(tf, ett_pgm_ack);
862
863                         proto_tree_add_uint(type_tree, hf_pgm_ack_sqn, tvb, 
864                                 offset, 4, ack.rx_max_sqn);
865                         proto_tree_add_uint(type_tree, hf_pgm_ack_bitmap, tvb, 
866                                 offset+4, 4, ack.bitmap);
867
868                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
869                                 break;
870                         offset += plen;
871
872                         dissect_pgmopts(tvb, offset, type_tree, pktname);
873
874                         break;
875                 }
876
877         }
878         if (isdata) {
879                 /*
880                  * Now see if there are any sub-dissectors, if so call them
881                  */
882                 offset = total_size(tvb, &pgmhdr);
883                 decode_pgm_ports(tvb, offset, pinfo, tree, &pgmhdr);
884         }
885         pktname = NULL;
886 }
887 static const true_false_string opts_present = {      
888         "Present",
889         "Not Present" 
890 };
891
892 /* Register all the bits needed with the filtering engine */
893 void 
894 proto_register_pgm(void)
895 {
896   static hf_register_info hf[] = {
897     { &hf_pgm_main_sport,
898       { "Source Port", "pgm.hdr.sport", FT_UINT16, BASE_DEC,
899           NULL, 0x0, "", HFILL }},
900     { &hf_pgm_main_dport,
901       { "Destination Port", "pgm.hdr.dport", FT_UINT16, BASE_DEC,
902           NULL, 0x0, "", HFILL }},
903     { &hf_pgm_main_type,
904       { "Type", "pgm.hdr.type", FT_UINT8, BASE_HEX,
905           VALS(type_vals), 0x0, "", HFILL }},
906     { &hf_pgm_main_opts,
907       { "Options", "pgm.hdr.opts", FT_UINT8, BASE_HEX,
908           NULL, 0x0, "", HFILL }},
909     { &hf_pgm_main_opts_opt,
910       { "Options", "pgm.hdr.opts.opt", FT_BOOLEAN, BASE_NONE,
911           TFS(&opts_present), PGM_OPT, "", HFILL }},
912     { &hf_pgm_main_opts_netsig,
913       { "Network Significant Options", "pgm.hdr.opts.netsig", 
914           FT_BOOLEAN, BASE_NONE,
915           TFS(&opts_present), PGM_OPT_NETSIG, "", HFILL }},
916     { &hf_pgm_main_opts_varlen,
917       { "Variable length Parity Packet Option", "pgm.hdr.opts.varlen", 
918           FT_BOOLEAN, BASE_NONE,
919           TFS(&opts_present), PGM_OPT_VAR_PKTLEN, "", HFILL }},
920     { &hf_pgm_main_opts_parity,
921       { "Parity", "pgm.hdr.opts.parity", FT_BOOLEAN, BASE_NONE,
922           TFS(&opts_present), PGM_OPT_PARITY, "", HFILL }},
923     { &hf_pgm_main_cksum,
924       { "Checksum", "pgm.hdr.cksum", FT_UINT16, BASE_HEX,
925         NULL, 0x0, "", HFILL }},
926     { &hf_pgm_main_gsi,
927       { "Global Source Identifier", "pgm.hdr.gsi", FT_BYTES, BASE_HEX, 
928           NULL, 0x0, "", HFILL }},
929     { &hf_pgm_main_tsdulen,
930       { "Transport Service Data Unit Length", "pgm.hdr.tsdulen", FT_UINT16, 
931           BASE_DEC, NULL, 0x0, "", HFILL }},
932     { &hf_pgm_spm_sqn,
933       { "Sequence number", "pgm.spm.sqn", FT_UINT32, BASE_HEX,
934           NULL, 0x0, "", HFILL }},
935     { &hf_pgm_spm_trail,
936       { "Trailing Edge Sequence Number", "pgm.spm.trail", FT_UINT32, BASE_HEX, 
937           NULL, 0x0, "", HFILL }},
938     { &hf_pgm_spm_lead,
939       { "Leading Edge Sequence Number", "pgm.spm.lead", FT_UINT32, BASE_HEX, 
940           NULL, 0x0, "", HFILL }},
941     { &hf_pgm_spm_pathafi,
942       { "Path NLA AFI", "pgm.spm.pathafi", FT_UINT16, BASE_DEC,
943           VALS(afn_vals), 0x0, "", HFILL }},
944     { &hf_pgm_spm_res,
945       { "Reserved", "pgm.spm.res", FT_UINT16, BASE_HEX,
946           NULL, 0x0, "", HFILL }},
947     { &hf_pgm_spm_path,
948       { "Path NLA", "pgm.spm.path", FT_IPv4, BASE_NONE,
949           NULL, 0x0, "", HFILL }},
950     { &hf_pgm_data_sqn,
951       { "Data Packet Sequence Number", "pgm.data.sqn", FT_UINT32, BASE_HEX, 
952           NULL, 0x0, "", HFILL }},
953     { &hf_pgm_data_trail,
954       { "Trailing Edge Sequence Number", "pgm.data.trail", FT_UINT32, BASE_HEX, 
955           NULL, 0x0, "", HFILL }},
956     { &hf_pgm_nak_sqn,
957       { "Requested Sequence Number", "pgm.nak.sqn", FT_UINT32, BASE_HEX, 
958           NULL, 0x0, "", HFILL }},
959     { &hf_pgm_nak_srcafi,
960       { "Source NLA AFI", "pgm.nak.srcafi", FT_UINT16, BASE_DEC,
961           VALS(afn_vals), 0x0, "", HFILL }},
962     { &hf_pgm_nak_srcres,
963       { "Reserved", "pgm.nak.srcres", FT_UINT16, BASE_HEX,
964           NULL, 0x0, "", HFILL }},
965     { &hf_pgm_nak_src,
966       { "Source NLA", "pgm.nak.src", FT_IPv4, BASE_NONE,
967           NULL, 0x0, "", HFILL }},
968     { &hf_pgm_nak_grpafi,
969       { "Multicast Group AFI", "pgm.nak.grpafi", FT_UINT16, BASE_DEC,
970           VALS(afn_vals), 0x0, "", HFILL }},
971     { &hf_pgm_nak_grpres,
972       { "Reserved", "pgm.nak.grpres", FT_UINT16, BASE_HEX,
973           NULL, 0x0, "", HFILL }},
974     { &hf_pgm_nak_grp,
975       { "Multicast Group NLA", "pgm.nak.grp", FT_IPv4, BASE_NONE,
976           NULL, 0x0, "", HFILL }},
977     { &hf_pgm_ack_sqn,
978       { "Maximum Received Sequence Number", "pgm.ack.maxsqn", FT_UINT32, 
979           BASE_HEX, NULL, 0x0, "", HFILL }},
980     { &hf_pgm_ack_bitmap,
981       { "Packet Bitmap", "pgm.ack.bitmap", FT_UINT32, BASE_HEX, 
982           NULL, 0x0, "", HFILL }},
983     { &hf_pgm_opt_type,
984       { "Type", "pgm.opts.type", FT_UINT8, BASE_HEX,
985           VALS(opt_vals), 0x0, "", HFILL }},
986     { &hf_pgm_opt_len,
987       { "Length", "pgm.opts.len", FT_UINT8, BASE_DEC,
988           NULL, 0x0, "", HFILL }},
989     { &hf_pgm_opt_tlen,
990       { "Total Length", "pgm.opts.tlen", FT_UINT16, BASE_DEC,
991           NULL, 0x0, "", HFILL }},
992     { &hf_pgm_genopt_type,
993       { "Type", "pgm.genopts.type", FT_UINT8, BASE_HEX,
994           VALS(opt_vals), 0x0, "", HFILL }},
995     { &hf_pgm_genopt_len,
996       { "Length", "pgm.genopts.len", FT_UINT8, BASE_DEC,
997           NULL, 0x0, "", HFILL }},
998     { &hf_pgm_genopt_opx,
999       { "Option Extensibility Bits", "pgm.genopts.opx", FT_UINT8, BASE_HEX,
1000           VALS(opx_vals), 0x0, "", HFILL }},
1001     { &hf_pgm_opt_parity_prm_po,
1002       { "Parity Parameters", "pgm.opts.parity_prm.op", FT_UINT8, BASE_HEX,
1003           NULL, 0x0, "", HFILL }},
1004     { &hf_pgm_opt_parity_prm_prmtgsz,
1005       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp",
1006           FT_UINT32, BASE_HEX,
1007           NULL, 0x0, "", HFILL }},
1008     { &hf_pgm_opt_join_res,
1009       { "Reserved", "pgm.opts.join.res", FT_UINT8, BASE_HEX,
1010           NULL, 0x0, "", HFILL }},
1011     { &hf_pgm_opt_join_minjoin,
1012       { "Minimum Sequence Number", "pgm.opts.join.min_join",
1013           FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1014     { &hf_pgm_opt_parity_grp_res,
1015       { "Reserved", "pgm.opts.parity_prm.op", FT_UINT8, BASE_HEX,
1016           NULL, 0x0, "", HFILL }},
1017     { &hf_pgm_opt_parity_grp_prmgrp,
1018       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp",
1019           FT_UINT32, BASE_HEX,
1020           NULL, 0x0, "", HFILL }},
1021     { &hf_pgm_opt_nak_res,
1022       { "Reserved", "pgm.opts.nak.op", FT_UINT8, BASE_HEX,
1023           NULL, 0x0, "", HFILL }},
1024     { &hf_pgm_opt_nak_list,
1025       { "List", "pgm.opts.nak.list", FT_BYTES, BASE_NONE,
1026           NULL, 0x0, "", HFILL }},
1027     { &hf_pgm_opt_ccdata_res,
1028       { "Reserved", "pgm.opts.ccdata.res", FT_UINT8, BASE_DEC,
1029           NULL, 0x0, "", HFILL }},
1030     { &hf_pgm_opt_ccdata_tsp,
1031       { "Time Stamp", "pgm.opts.ccdata.tstamp", FT_UINT16, BASE_HEX,
1032           NULL, 0x0, "", HFILL }},
1033     { &hf_pgm_opt_ccdata_afi,
1034       { "Acker AFI", "pgm.opts.ccdata.afi", FT_UINT16, BASE_DEC,
1035           VALS(afn_vals), 0x0, "", HFILL }},
1036     { &hf_pgm_opt_ccdata_res2,
1037       { "Reserved", "pgm.opts.ccdata.res2", FT_UINT16, BASE_DEC,
1038           NULL, 0x0, "", HFILL }},
1039     { &hf_pgm_opt_ccdata_acker,
1040       { "Acker", "pgm.opts.ccdata.acker", FT_IPv4, BASE_NONE,
1041           NULL, 0x0, "", HFILL }},
1042     { &hf_pgm_opt_ccfeedbk_res,
1043       { "Reserved", "pgm.opts.ccdata.res", FT_UINT8, BASE_DEC,
1044           NULL, 0x0, "", HFILL }},
1045     { &hf_pgm_opt_ccfeedbk_tsp,
1046       { "Time Stamp", "pgm.opts.ccdata.tstamp", FT_UINT16, BASE_HEX,
1047           NULL, 0x0, "", HFILL }},
1048     { &hf_pgm_opt_ccfeedbk_afi,
1049       { "Acker AFI", "pgm.opts.ccdata.afi", FT_UINT16, BASE_DEC,
1050           VALS(afn_vals), 0x0, "", HFILL }},
1051     { &hf_pgm_opt_ccfeedbk_lossrate,
1052       { "Loss Rate", "pgm.opts.ccdata.lossrate", FT_UINT16, BASE_HEX,
1053           NULL, 0x0, "", HFILL }},
1054     { &hf_pgm_opt_ccfeedbk_acker,
1055       { "Acker", "pgm.opts.ccdata.acker", FT_IPv4, BASE_NONE,
1056           NULL, 0x0, "", HFILL }},
1057   };
1058   static gint *ett[] = {
1059     &ett_pgm,
1060         &ett_pgm_optbits,
1061         &ett_pgm_spm,
1062         &ett_pgm_data,
1063         &ett_pgm_nak,
1064         &ett_pgm_ack,
1065         &ett_pgm_opts,
1066         &ett_pgm_opts_join,
1067         &ett_pgm_opts_parityprm,
1068         &ett_pgm_opts_paritygrp,
1069         &ett_pgm_opts_naklist,
1070         &ett_pgm_opts_ccdata,
1071   };
1072   module_t *pgm_module;
1073
1074   proto_pgm = proto_register_protocol("Pragmatic General Multicast",
1075                                        "PGM", "pgm");
1076
1077   proto_register_field_array(proto_pgm, hf, array_length(hf));
1078   proto_register_subtree_array(ett, array_length(ett));
1079
1080         /* subdissector code */
1081   subdissector_table = register_dissector_table("pgm.port");
1082   register_heur_dissector_list("pgm", &heur_subdissector_list);
1083
1084   /*
1085    * Register configuration preferences for UDP encapsulation
1086    * (Note: Initially the ports are set to zero so the 
1087    *        dissecting of PGM encapsulated in UPD packets
1088    *        is off by default)
1089    */
1090    pgm_module = prefs_register_protocol(proto_pgm, proto_rereg_pgm);
1091
1092    prefs_register_uint_preference(pgm_module, "udp.encap_ucast_port",
1093                 "PGM Encap Unicast Port (Default 3055)", 
1094                 "PGM Encap is PGM packets encapsulated in UDP packets"
1095                 " (Note: This is option is off by default", 
1096                 10, &udp_encap_ucast_port);
1097    old_encap_ucast_port = udp_encap_ucast_port;
1098
1099    prefs_register_uint_preference(pgm_module, "udp.encap_mcast_port",
1100                 "PGM Encap Multicast Port (Default 3056)", 
1101                 "PGM Encap is PGM packets encapsulated in UDP packets"
1102                 " (Note: This is option is off by default", 
1103                 10, &udp_encap_mcast_port);
1104
1105    old_encap_mcast_port = udp_encap_mcast_port;
1106 }
1107
1108 /* The registration hand-off routine */
1109 void
1110 proto_reg_handoff_pgm(void)
1111 {
1112
1113   /*
1114    * Set up PGM Encap dissecting, which is off by default
1115    */
1116   dissector_add("udp.port", udp_encap_ucast_port, dissect_pgm, proto_pgm);
1117   dissector_add("udp.port", udp_encap_mcast_port, dissect_pgm, proto_pgm);
1118
1119   dissector_add("ip.proto", IP_PROTO_PGM, dissect_pgm, proto_pgm);
1120
1121 }
1122 void
1123 proto_rereg_pgm(void)
1124 {
1125         /*
1126          * Remove the old ones
1127          */
1128         dissector_delete("udp.port", old_encap_ucast_port, dissect_pgm);
1129         dissector_delete("udp.port", old_encap_mcast_port, dissect_pgm);
1130
1131         /*
1132          * Set the new ones
1133          */
1134         dissector_add("udp.port", udp_encap_ucast_port, dissect_pgm, proto_pgm);
1135         dissector_add("udp.port", udp_encap_mcast_port, dissect_pgm, proto_pgm);
1136 }