PGM (Pragmatic General Multicast - RFC 2705) support, from Steve Dickson.
[obnox/wireshark/wip.git] / packet-pgm.c
1 /* packet-pgm.c
2  * Routines for pgm packet disassembly
3  * RFC 2705
4  *
5  * $Id: packet-pgm.c,v 1.1 2001/07/12 20:16:28 guy Exp $
6  * 
7  * Copyright (c) 2000 by Talarian Corp
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
11  * Copyright 1999 Gerald Combs
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #ifdef HAVE_SYS_TYPES_H
33 #include <sys/types.h>
34 #endif
35
36 #ifdef HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
42 #endif
43
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>
46 #endif
47
48 #ifdef HAVE_NETDB_H
49 # include <netdb.h>
50 #endif
51
52
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <ctype.h>
56 #include <time.h>
57 #include <string.h>
58 #include "packet.h"
59 #include "packet-pgm.h"
60 #if 0
61 #include "resolv.h"
62 #include "prefs.h"
63 #include "strutil.h"
64 #endif 
65
66 #include "proto.h"
67 #ifndef IP_PROTO_PGM
68 #define IP_PROTO_PGM 113
69 #endif
70
71 void proto_reg_handoff_pgm(void);
72 extern void decode_tcp_ports(tvbuff_t *, int , packet_info *,
73     proto_tree *, int, int);
74
75
76 static int proto_pgm = -1;
77 static int ett_pgm = -1;
78 static int ett_pgm_optbits = -1;
79 static int ett_pgm_opts = -1;
80 static int ett_pgm_spm = -1;
81 static int ett_pgm_data = -1;
82 static int ett_pgm_nak = -1;
83 static int ett_pgm_opts_join = -1;
84 static int ett_pgm_opts_parityprm = -1;
85 static int ett_pgm_opts_paritygrp = -1;
86 static int ett_pgm_opts_naklist = -1;
87
88 static int hf_pgm_main_sport = -1;
89 static int hf_pgm_main_dport = -1;
90 static int hf_pgm_main_type = -1;
91 static int hf_pgm_main_opts = -1;
92 static int hf_pgm_main_opts_opt = -1;
93 static int hf_pgm_main_opts_netsig = -1;
94 static int hf_pgm_main_opts_varlen = -1;
95 static int hf_pgm_main_opts_parity = -1;
96 static int hf_pgm_main_cksum = -1;
97 static int hf_pgm_main_gsi = -1;
98 static int hf_pgm_main_tsdulen = -1;
99 static int hf_pgm_spm_sqn = -1;
100 static int hf_pgm_spm_lead = -1;
101 static int hf_pgm_spm_trail = -1;
102 static int hf_pgm_spm_pathafi = -1;
103 static int hf_pgm_spm_res = -1;
104 static int hf_pgm_spm_path = -1;
105 static int hf_pgm_data_sqn = -1;
106 static int hf_pgm_data_trail = -1;
107 static int hf_pgm_nak_sqn = -1;
108 static int hf_pgm_nak_srcafi = -1;
109 static int hf_pgm_nak_srcres = -1;
110 static int hf_pgm_nak_src = -1;
111 static int hf_pgm_nak_grpafi = -1;
112 static int hf_pgm_nak_grpres = -1;
113 static int hf_pgm_nak_grp = -1;
114
115 static int hf_pgm_opt_type = -1;
116 static int hf_pgm_opt_len = -1;
117 static int hf_pgm_opt_tlen = -1;
118
119 static int hf_pgm_genopt_type = -1;
120 static int hf_pgm_genopt_len = -1;
121 static int hf_pgm_genopt_opx = -1;
122 static int hf_pgm_genopt_res = -1;
123
124 static int hf_pgm_opt_join_type = -1;
125 static int hf_pgm_opt_join_len = -1;
126 static int hf_pgm_opt_join_opx = -1;
127 static int hf_pgm_opt_join_res = -1;
128 static int hf_pgm_opt_join_minjoin = -1;
129
130 static int hf_pgm_opt_parity_prm_type = -1;
131 static int hf_pgm_opt_parity_prm_len = -1;
132 static int hf_pgm_opt_parity_prm_opx = -1;
133 static int hf_pgm_opt_parity_prm_po = -1;
134 static int hf_pgm_opt_parity_prm_prmtgsz = -1;
135
136 static int hf_pgm_opt_parity_grp_type = -1;
137 static int hf_pgm_opt_parity_grp_len = -1;
138 static int hf_pgm_opt_parity_grp_opx = -1;
139 static int hf_pgm_opt_parity_grp_res = -1;
140 static int hf_pgm_opt_parity_grp_prmgrp = -1;
141
142 static int hf_pgm_opt_curr_tgsize_type = -1;
143 static int hf_pgm_opt_curr_tgsize_len = -1;
144 static int hf_pgm_opt_curr_tgsize_opx = -1;
145 static int hf_pgm_opt_curr_tgsize_res = -1;
146 static int hf_pgm_opt_curr_tgsize_prmatgsz = -1;
147
148 static int hf_pgm_opt_nak_type = -1;
149 static int hf_pgm_opt_nak_len = -1;
150 static int hf_pgm_opt_nak_opx = -1;
151 static int hf_pgm_opt_nak_res = -1;
152 static int hf_pgm_opt_nak_list = -1;
153
154 inline char *
155 gsistr(char *gsi, int len)
156 {
157          static char msg[256];
158          char *p = msg;
159          int i;
160
161         for (i = 0; i <= 255 && i < len; i++) {
162                 sprintf(p,"%02x",(nchar_t)gsi[i]);
163                 p += 2;
164         }
165         return(msg);
166 }
167 inline char *
168 optsstr(nchar_t opts)
169 {
170          static char msg[256];
171          char *p = msg, *str;
172
173         if (opts == 0)
174                 return("");
175
176         if (opts & PGM_OPT){
177                 sprintf(p, "Present");
178                 p += strlen("Present");
179         }
180         if (opts & PGM_OPT_NETSIG){
181                 if (p != msg)
182                         str = ",NetSig";
183                 else
184                         str = "NetSig";
185                 sprintf(p, str);
186                 p += strlen(str);
187         }
188         if (opts & PGM_OPT_VAR_PKTLEN){
189                 if (p != msg)
190                         str = ",VarLen";
191                 else
192                         str = "VarLen";
193                 sprintf(p, str);
194                 p += strlen(str);
195         }
196         if (opts & PGM_OPT_PARITY){
197                 if (p != msg)
198                         str = ",Parity";
199                 else
200                         str = "Parity";
201                 sprintf(p, str);
202                 p += strlen(str);
203         }
204         if (p == msg) {
205                 sprintf(p, "0x%x", opts);
206         }
207         return(msg);
208 }
209 inline char *
210 opttypes(nchar_t opt)
211 {
212          static char msg[128];
213
214         if (opt == PGM_OPT_LENGTH)
215                 return("Length");
216         if (opt == PGM_OPT_END)
217                 return("End");
218         if (opt == PGM_OPT_FRAGMENT)
219                 return("Fragment");
220         if (opt == PGM_OPT_NAK_LIST)
221                 return("NakList");
222         if (opt == PGM_OPT_JOIN)
223                 return("Join");
224         if (opt == PGM_OPT_REDIRECT)
225                 return("ReDirect");
226         if (opt == PGM_OPT_SYN)
227                 return("Syn");
228         if (opt == PGM_OPT_FIN)
229                 return("Fin");
230         if (opt == PGM_OPT_RST)
231                 return("Rst");
232         if (opt == PGM_OPT_PARITY_PRM)
233                 return("ParityPrm");
234         if (opt == PGM_OPT_PARITY_GRP)
235                 return("ParityGrp");
236         if (opt == PGM_OPT_CURR_TGSIZE)
237                 return("CurrTgsiz");
238
239         sprintf(msg, "0x%x", opt);
240         return(msg);
241 }
242 inline char *
243 opxbits(nchar_t opt)
244 {
245          static char msg[128];
246
247         if (opt == 0)
248                 return("");
249
250         if (opt == PGM_OPX_IGNORE)
251                 return("Ignore");
252         if (opt == PGM_OPX_INVAL)
253                 return("Inval");
254         if (opt == PGM_OPX_DISCARD)
255                 return("DisCard");
256
257         sprintf(msg, "0x%x", opt);
258         return(msg);
259 }
260 char *pktname = NULL;
261
262 static void
263 dissect_pgmopts(tvbuff_t *tvb, int offset, proto_tree *tree)
264 {
265         proto_item *tf;
266         proto_tree *opts_tree = NULL;
267         proto_tree *opt_tree = NULL;
268         pgm_opt_length_t opts;
269         pgm_opt_generic_t genopts;
270         int theend = 0, firsttime = 1;
271
272         tvb_memcpy(tvb, (guint8 *)&opts, offset, sizeof(opts));
273         opts.total_len = ntohs(opts.total_len);
274
275         tf = proto_tree_add_text(tree, tvb, offset, 
276                 opts.total_len, 
277                 "%s Options (Total Length %d)", pktname, opts.total_len);
278         opts_tree = proto_item_add_subtree(tf, ett_pgm_opts);
279         proto_tree_add_uint_format(opts_tree, hf_pgm_opt_type, tvb, 
280                 offset, 4, opts.type, 
281                 "Option: %-9s (0x%x), Length: %d, Total Length: %d", 
282                 opttypes(opts.type), opts.type, opts.len, opts.total_len);
283
284         offset += 4;
285         for (opts.total_len -= 4; opts.total_len > 0;){
286                 tvb_memcpy(tvb, (guint8 *)&genopts, offset, sizeof(genopts));
287                 if (genopts.type & PGM_OPT_END)  {
288                         genopts.type &= ~PGM_OPT_END;
289                         theend = 1;
290                 }
291                 tf = proto_tree_add_uint_format(opts_tree, hf_pgm_genopt_type, tvb, 
292                         offset, genopts.len, genopts.type, 
293                         "Option: %-9s (0x%x), Length: %d", opttypes(genopts.type), 
294                                 genopts.type, genopts.len );
295                 if (genopts.len == 0)
296                         break;
297
298                 switch(genopts.type) {
299                 case PGM_OPT_JOIN:{
300                         pgm_opt_join_t optdata;
301
302                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
303                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_join);
304
305                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_join_type, 
306                                 tvb, offset, 1, optdata.type, "Type: %s (0x%x)", 
307                                 opttypes(optdata.type), optdata.type);
308
309                         proto_tree_add_uint(opt_tree, hf_pgm_opt_join_len, tvb, 
310                                 offset+1, 1, optdata.len);
311
312                         proto_tree_add_uint(opt_tree, hf_pgm_opt_join_opx, tvb, 
313                                 offset+2, 1, optdata.opx);
314
315                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_join_res, tvb, 
316                                 offset+3, 1, optdata.opx, "Reserved: 0x%x", optdata.res);
317
318                         proto_tree_add_uint(opt_tree, hf_pgm_opt_join_minjoin, tvb, 
319                                 offset+4, 4, ntohl(optdata.opt_join_min));
320
321                         break;
322                 }
323                 case PGM_OPT_PARITY_PRM:{
324                         pgm_opt_parity_prm_t optdata;
325
326                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
327                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_parityprm);
328
329                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_parity_prm_type, 
330                                 tvb, offset, 1, optdata.type, "Type: %s (0x%x)", 
331                                 opttypes(optdata.type), optdata.type);
332
333                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_prm_len, tvb, 
334                                 offset+1, 1, optdata.len);
335
336                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_parity_prm_opx, 
337                                 tvb, offset+2, 1, optdata.opx, 
338                                 "Extensibility Bits: %s (0x%x)", opxbits(optdata.opx), 
339                                 optdata.opx);
340
341                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_prm_po, tvb, 
342                                 offset+3, 1, optdata.po);
343
344                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_prm_prmtgsz,
345                                 tvb, offset+4, 4, ntohl(optdata.prm_tgsz));
346
347                         break;
348                 }
349                 case PGM_OPT_PARITY_GRP:{
350                         pgm_opt_parity_grp_t optdata;
351
352                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
353                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_paritygrp);
354
355                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_parity_prm_type, 
356                                 tvb, offset, 1, optdata.type, "Type: %s (0x%x)", 
357                                 opttypes(optdata.type), optdata.type);
358
359                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_prm_len, tvb, 
360                                 offset+1, 1, optdata.len);
361
362                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_parity_grp_opx, 
363                                 tvb, offset+2, 1, optdata.opx, 
364                                 "Extensibility Bits: %s (0x%x)", opxbits(optdata.opx), 
365                                 optdata.opx);
366
367                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_grp_res, tvb, 
368                                 offset+3, 1, optdata.res);
369
370                         proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_grp_prmgrp,
371                                 tvb, offset+4, 4, ntohl(optdata.prm_grp));
372
373                         break;
374                 }
375                 case PGM_OPT_NAK_LIST:{
376                         pgm_opt_nak_list_t optdata;
377                         nlong_t naklist[PGM_MAX_NAK_LIST_SZ+1];
378                         char nakbuf[8192], *ptr;
379                         int i, j, naks, soffset = 0;
380
381                         tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
382                         opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_naklist);
383
384                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_nak_type, tvb, 
385                                 offset, 1, optdata.type, "Type: %s (0x%x)", 
386                                 opttypes(optdata.type), optdata.type);
387
388                         proto_tree_add_uint(opt_tree, hf_pgm_opt_nak_len, tvb, 
389                                 offset+1, 1, optdata.len);
390
391                         proto_tree_add_uint_format(opt_tree, hf_pgm_opt_nak_opx, 
392                                 tvb, offset+2, 1, optdata.opx, 
393                                 "Extensibility Bits: %s (0x%x)", opxbits(optdata.opx), 
394                                 optdata.opx);
395
396                         proto_tree_add_uint(opt_tree, hf_pgm_opt_nak_res, tvb, 
397                                 offset+3, 1, optdata.res);
398
399                         optdata.len -= sizeof(pgm_opt_nak_list_t);
400                         tvb_memcpy(tvb, (guint8 *)naklist, offset+4, optdata.len);
401                         naks = (optdata.len/sizeof(nlong_t));
402                         ptr = nakbuf;
403                         j = 0;
404                         /*
405                          * Print out 8 per line 
406                          */
407                         for (i=0; i < naks; i++) {
408                                 sprintf(nakbuf+soffset, "0x%lx ",
409                                     (unsigned long)ntohl(naklist[i]));
410                                 soffset = strlen(nakbuf);
411                                 if ((++j % 8) == 0) {
412                                         if (firsttime) {
413                                                 proto_tree_add_bytes_format(opt_tree, 
414                                                         hf_pgm_opt_nak_list, tvb, offset+4, optdata.len,
415                                                         nakbuf, "List(%d): %s", naks, nakbuf);
416                                                         soffset = 0;
417                                         } else {
418                                                 proto_tree_add_bytes_format(opt_tree, 
419                                                         hf_pgm_opt_nak_list, tvb, offset+4, optdata.len, 
420                                                         nakbuf, "List: %s", nakbuf);
421                                                         soffset = 0;
422                                         }
423                                         firsttime = 0;
424                                 }
425                         }
426                         if (soffset) {
427                                 if (firsttime) {
428                                         proto_tree_add_bytes_format(opt_tree, 
429                                                 hf_pgm_opt_nak_list, tvb, offset+4, optdata.len,
430                                                 nakbuf, "List(%d): %s", naks, nakbuf);
431                                                 soffset = 0;
432                                 } else {
433                                         proto_tree_add_bytes_format(opt_tree, 
434                                                 hf_pgm_opt_nak_list, tvb, offset+4, optdata.len, 
435                                                 nakbuf, "List: %s", nakbuf);
436                                                 soffset = 0;
437                                 }
438                         }
439                         break;
440                 }
441                 }
442                 offset += genopts.len;
443                 opts.total_len -= genopts.len;
444
445         }
446 }
447 /*
448  * dissect_pgm - The dissector for Pragmatic General Multicast
449  */
450 static void
451 dissect_pgm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
452 {
453         proto_tree *pgm_tree = NULL;
454         proto_tree *opt_tree = NULL;
455         proto_tree *type_tree = NULL;
456         proto_item *tf;
457         struct hostent *hp;
458         pgm_t pgmhdr;
459         pgm_spm_t spm;
460         pgm_data_t data;
461         pgm_nak_t nak;
462         int offset = 0;
463         guint hlen, plen;
464         proto_item *ti;
465         char buf[512];
466         struct in_addr inaddr;
467         char tmpaddr[16];
468
469         if (check_col(pinfo->fd, COL_PROTOCOL))
470                 col_set_str(pinfo->fd, COL_PROTOCOL, "PGM");
471
472         /* Clear out the Info column. */
473         if (check_col(pinfo->fd, COL_INFO))
474                 col_clear(pinfo->fd, COL_INFO);
475
476         tvb_memcpy(tvb, (guint8 *)&pgmhdr, offset, sizeof(pgm_t));
477         hlen = sizeof(pgm_t);
478         pgmhdr.sport = ntohs(pgmhdr.sport);
479         pgmhdr.dport = ntohs(pgmhdr.dport);
480         pgmhdr.tsdulen = ntohs(pgmhdr.tsdulen);
481
482         switch(pgmhdr.type) {
483         case PGM_SPM_PCKT:
484                 plen = sizeof(pgm_spm_t);
485                 tvb_memcpy(tvb, (guint8 *)&spm, sizeof(pgm_t), plen);
486                 pktname = "SPM";
487                 spm_ntoh(&spm);
488                 inaddr.s_addr = htonl(spm.path);
489                 sprintf(buf, "SPM: sqn 0x%x path %s", spm.sqn, inet_ntoa(inaddr));
490                 break;
491
492         case PGM_RDATA_PCKT:
493                 pktname = "RDATA";
494                 /* FALLTHUR */
495         case PGM_ODATA_PCKT:
496                 if (pktname == NULL)
497                         pktname = "ODATA";
498                 plen = sizeof(pgm_data_t);
499                 tvb_memcpy(tvb, (guint8 *)&data, sizeof(pgm_t), plen);
500                 data_ntoh(&data);
501                 sprintf(buf, "%s: sqn 0x%x tsdulen %d", pktname, data.sqn, 
502                         pgmhdr.tsdulen);
503                 break;
504
505         case PGM_NAK_PCKT:
506                 pktname = "NAK";
507                 /* FALLTHUR */
508         case PGM_NNAK_PCKT:
509                 if (pktname == NULL)
510                         pktname = "NNAK";
511                 /* FALLTHUR */
512         case PGM_NCF_PCKT:
513                 if (pktname == NULL)
514                         pktname = "NCF";
515                 plen = sizeof(pgm_nak_t);
516                 tvb_memcpy(tvb, (guint8 *)&nak, sizeof(pgm_t), plen);
517                 nak_ntoh(&nak);
518                 inaddr.s_addr = htonl(nak.src);
519                 strcpy(tmpaddr, inet_ntoa(inaddr));
520                 inaddr.s_addr = htonl(nak.grp);
521                 sprintf(buf, "%s: sqn 0x%x src %s grp %s", pktname, nak.sqn, 
522                         tmpaddr, inet_ntoa(inaddr)); 
523
524                 break;
525         default:
526                 return;
527         }
528
529         if (check_col(pinfo->fd, COL_INFO)) {
530                 col_add_fstr(pinfo->fd, COL_INFO, "%s", buf);
531         }
532         if (tree) {
533                 sprintf(buf, "Pragmatic General Multicast: Type %s (%d)"
534                     " SrcPort %u, DstPort %u, GSI %s", pktname,
535                         pgmhdr.type, pgmhdr.sport, pgmhdr.dport, gsistr(pgmhdr.gsi, 6));
536                 ti = proto_tree_add_protocol_format(tree, proto_pgm, 
537                         tvb, offset, hlen, "%s", buf);
538 #if 0
539                 ti = proto_tree_add_item(tree, proto_pgm, tvb, offset, hlen, FALSE);
540 #endif
541
542                 pgm_tree = proto_item_add_subtree(ti, ett_pgm);
543                 proto_tree_add_uint_format(pgm_tree, hf_pgm_main_sport, tvb, offset, 2,
544                         pgmhdr.sport, "Source port: %u", pgmhdr.sport);
545                 proto_tree_add_uint_format(pgm_tree, hf_pgm_main_dport, tvb, offset+2, 
546                         2, pgmhdr.dport, "Destination port: %u", pgmhdr.dport);
547                 tf = proto_tree_add_uint_format(pgm_tree, hf_pgm_main_type, tvb, 
548                         offset+4, 1, pgmhdr.type, "Packet type: %s (%d)", pktname, 
549                         pgmhdr.type);
550
551                 tf = proto_tree_add_uint_format(pgm_tree, hf_pgm_main_opts, tvb, 
552                         offset+5, 1, pgmhdr.opts, "Options: %s (0x%x)", 
553                         optsstr(pgmhdr.opts), pgmhdr.opts);
554                 opt_tree = proto_item_add_subtree(tf, ett_pgm_optbits);
555
556                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_opt, tvb, 
557                         offset+5, 1, (pgmhdr.opts & PGM_OPT));
558                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_netsig, tvb, 
559                         offset+5, 1, (pgmhdr.opts & PGM_OPT_NETSIG));
560                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_varlen, tvb, 
561                         offset+5, 1, (pgmhdr.opts & PGM_OPT_VAR_PKTLEN));
562                 proto_tree_add_boolean(opt_tree, hf_pgm_main_opts_parity, tvb, 
563                         offset+5, 1, (pgmhdr.opts & PGM_OPT_PARITY));
564
565                 proto_tree_add_uint_format(pgm_tree, hf_pgm_main_cksum, tvb, offset+6, 
566                         2, pgmhdr.cksum, "Check Sum: 0x%x", pgmhdr.cksum);
567                 proto_tree_add_bytes(pgm_tree, hf_pgm_main_gsi, tvb, offset+8, 
568                         6, pgmhdr.gsi);
569                 proto_tree_add_uint_format(pgm_tree, hf_pgm_main_tsdulen, tvb, 
570                         offset+14, 2, pgmhdr.tsdulen, "Transport Service Data Unit: %u", 
571                         pgmhdr.tsdulen);
572
573                 offset = sizeof(pgm_t);
574                 tf = proto_tree_add_text(pgm_tree, tvb, offset, plen, "%s Packet",
575                         pktname);
576                 switch(pgmhdr.type) {
577                 case PGM_SPM_PCKT:
578                         type_tree = proto_item_add_subtree(tf, ett_pgm_spm);
579
580                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
581                                 offset, 4, spm.sqn, "Sequence Number: 0x%x", spm.sqn);
582                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
583                                 offset+4, 4, spm.trail, "Trailing Edge Sequence Number: 0x%x", 
584                                 spm.trail);
585                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
586                                 offset+8, 4, spm.lead, "Leading Edge Sequence Number: 0x%x", 
587                                 spm.lead);
588                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
589                                 offset+10, 2, spm.path_afi, 
590                                 "Network Layer Address (Family Indicator): 0x%x", 
591                                 spm.path_afi);
592                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
593                                 offset+12, 2, spm.res, "Reserved: 0x%x", spm.res);
594                         inaddr.s_addr = htonl(spm.path);
595                         hp = gethostbyaddr((const char *)&inaddr, 
596                                 sizeof(struct in_addr), AF_INET);
597                         if (hp)
598                                 sprintf(buf, "Network Layer Address Path: %s (%s)",
599                                         inet_ntoa(inaddr), hp->h_name);
600                         else
601                                 sprintf(buf, "Network Layer Address Path: %s",
602                                         inet_ntoa(inaddr));
603                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
604                                 offset+14, 4, spm.sqn, "%s", buf);
605
606                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
607                                 break;
608                         offset += plen;
609
610                         dissect_pgmopts(tvb, offset, type_tree);
611
612                         break;
613
614                 case PGM_RDATA_PCKT:
615                 case PGM_ODATA_PCKT:
616                         type_tree = proto_item_add_subtree(tf, ett_pgm_data);
617
618                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
619                                 offset, 4, data.sqn, "Sequence Number: 0x%x", spm.sqn);
620                         proto_tree_add_uint_format(type_tree, hf_pgm_spm_sqn, tvb, 
621                                 offset+4, 4, data.trail, "Trailing Edge Sequence Number: 0x%x", 
622                                 data.trail);
623
624                         if ((pgmhdr.opts & PGM_OPT) == TRUE) {
625                                 offset += plen;;
626                                 dissect_pgmopts(tvb, offset, type_tree);
627                         }
628                         /*
629                          * Now see if there are any sub-dissectors, of so call them
630                          */
631                         decode_tcp_ports(tvb, offset, pinfo, tree, 
632                                 pgmhdr.sport, pgmhdr.dport);
633                         break;
634
635                 case PGM_NAK_PCKT:
636                 case PGM_NNAK_PCKT:
637                 case PGM_NCF_PCKT:
638                         type_tree = proto_item_add_subtree(tf, ett_pgm_nak);
639
640                         proto_tree_add_uint(type_tree, hf_pgm_nak_sqn, tvb, 
641                                 offset, 4, nak.sqn);
642                         proto_tree_add_uint(type_tree, hf_pgm_nak_srcafi, tvb, 
643                                 offset+4, 2, nak.src_afi);
644                         proto_tree_add_uint(type_tree, hf_pgm_nak_srcres, tvb, 
645                                 offset+6, 2, nak.src_res);
646
647                         inaddr.s_addr = htonl(nak.src);
648                         hp = gethostbyaddr((const char *)&inaddr, 
649                                 sizeof(struct in_addr), AF_INET);
650                         if (hp)
651                                 sprintf(buf, "Source Network Layer Address: %s (%s)",
652                                         inet_ntoa(inaddr), hp->h_name);
653                         else
654                                 sprintf(buf, "Source Network Layer Address Path: %s",
655                                         inet_ntoa(inaddr));
656                         proto_tree_add_uint_format(type_tree, hf_pgm_nak_src, tvb, 
657                                 offset+8, 4, nak.src, "%s", buf);
658
659                         proto_tree_add_uint_format(type_tree, hf_pgm_nak_grpafi, tvb, 
660                                 offset+12, 2, nak.grp_afi, 
661                                 "Multicast group Network Layer Address (Family Indicator):"
662                                 " 0x%x", nak.grp_afi);
663                         proto_tree_add_uint_format(type_tree, hf_pgm_nak_grpres, tvb, 
664                                 offset+14, 2, nak.grp_res, "Reserved: 0x%x", nak.grp_res);
665
666                         inaddr.s_addr = htonl(nak.grp);
667                         proto_tree_add_uint_format(type_tree, hf_pgm_nak_grp, tvb, 
668                                 offset+16, 4, nak.grp, 
669                                 "Multicast group Network Layer Address Path: %s",
670                                 inet_ntoa(inaddr));
671
672                         if ((pgmhdr.opts & PGM_OPT) == FALSE)
673                                 break;
674                         offset += plen;
675
676                         dissect_pgmopts(tvb, offset, type_tree);
677
678                         break;
679                 }
680
681         }
682         pktname = NULL;
683 }
684 static const true_false_string opts_present = {      
685         "Present",
686         "Not Present" 
687 };
688
689 /* Register all the bits needed with the filtering engine */
690 void 
691 proto_register_pgm(void)
692 {
693   static hf_register_info hf[] = {
694     { &hf_pgm_main_sport,
695       { "Source Port", "pgm.hdr.sport", FT_UINT16, BASE_HEX, NULL, 0x0, 
696                 "", HFILL }},
697     { &hf_pgm_main_dport,
698       { "Destination Port", "pgm.hdr.dport", FT_UINT16, BASE_HEX, NULL, 0x0, 
699                 "", HFILL }},
700     { &hf_pgm_main_type,
701       { "Type", "pgm.hdr.type", FT_UINT8, BASE_HEX, NULL, 0x0, 
702                 "", HFILL }},
703     { &hf_pgm_main_opts,
704       { "Options", "pgm.hdr.opts", FT_UINT8, BASE_HEX, NULL, 0x0, 
705                 "", HFILL }},
706     { &hf_pgm_main_opts_opt,
707       { "Options", "pgm.hdr.opts.opt", FT_BOOLEAN, BASE_HEX, 
708           TFS(&opts_present), PGM_OPT, "", HFILL }},
709     { &hf_pgm_main_opts_netsig,
710       { "Network Significant Options", "pgm.hdr.opts.netsig", 
711           FT_BOOLEAN, BASE_HEX, TFS(&opts_present), PGM_OPT_NETSIG, "", HFILL }},
712     { &hf_pgm_main_opts_varlen,
713       { "Variable lenght Parity Packet Option", "pgm.hdr.opts.varlen", 
714           FT_BOOLEAN, BASE_HEX, TFS(&opts_present), PGM_OPT_VAR_PKTLEN, "", HFILL }},
715     { &hf_pgm_main_opts_parity,
716       { "Parity", "pgm.hdr.opts.parity", FT_BOOLEAN, BASE_HEX, 
717           TFS(&opts_present), PGM_OPT_PARITY, "", HFILL }},
718     { &hf_pgm_main_cksum,
719       { "Checksum", "pgm.hdr.cksum", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
720     { &hf_pgm_main_gsi,
721       { "Global Source Identifier", "pgm.hdr.gsi", FT_BYTES, BASE_HEX, 
722           NULL, 0x0, "", HFILL }},
723     { &hf_pgm_main_tsdulen,
724       { "Transport Service Data Unit", "pgm.hdr.tsdulen", FT_UINT8, BASE_HEX,
725           NULL, 0x0, "", HFILL }},
726     { &hf_pgm_spm_sqn,
727       { "Sequence number", "pgm.spm.sqn", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
728     { &hf_pgm_spm_trail,
729       { "Trailing Edge Sequence Number", "pgm.spm.trail", FT_UINT32, BASE_HEX, 
730           NULL, 0x0, "", HFILL }},
731     { &hf_pgm_spm_lead,
732       { "Leading Edge Sequence Number", "pgm.spm.lead", FT_UINT32, BASE_HEX, 
733           NULL, 0x0, "", HFILL }},
734     { &hf_pgm_spm_pathafi,
735       { "NLA AFI (IPv4 is set to 1)", "pgm.spm.pathafi", FT_UINT16, BASE_HEX, 
736           NULL, 0x0, "", HFILL }},
737     { &hf_pgm_spm_res,
738       { "Reserved", "pgm.spm.res", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
739     { &hf_pgm_spm_path,
740       { "Path NLA", "pgm.spm.path", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
741     { &hf_pgm_data_sqn,
742       { "Data Packet Sequence Number", "pgm.data.sqn", FT_UINT32, BASE_HEX, 
743           NULL, 0x0, "", HFILL }},
744     { &hf_pgm_data_trail,
745       { "Trailing Edge Sequence Number", "pgm.data.trail", FT_UINT32, BASE_HEX, 
746           NULL, 0x0, "", HFILL }},
747     { &hf_pgm_nak_sqn,
748       { "Requested Sequence Number", "pgm.nak.sqn", FT_UINT32, BASE_HEX, 
749           NULL, 0x0, "", HFILL }},
750     { &hf_pgm_nak_srcafi,
751       { "Source Network Layer Address (Family Indicator)", "pgm.nak.srcafi", 
752           FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
753     { &hf_pgm_nak_srcres,
754       { "Reserved", "pgm.nak.srcres", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
755     { &hf_pgm_nak_src,
756       { "Source NLA", "pgm.nak.src", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
757     { &hf_pgm_nak_grpafi,
758       { "Multicast group AFI", "pgm.nak.grpafi", FT_UINT16, BASE_HEX, 
759           NULL, 0x0, "", HFILL }},
760     { &hf_pgm_nak_grpres,
761       { "Reserved", "pgm.nak.grpres", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
762     { &hf_pgm_nak_grp,
763       { "Multicast Group NLA", "pgm.nak.grp", FT_UINT32, BASE_HEX, 
764           NULL, 0x0, "", HFILL }},
765     { &hf_pgm_opt_type,
766       { "Type", "pgm.opts.type", FT_UINT8, BASE_HEX, NULL, 0x0, 
767                 "", HFILL }},
768     { &hf_pgm_opt_len,
769       { "Length", "pgm.opts.len", FT_UINT8, BASE_DEC, NULL, 0x0, 
770                 "", HFILL }},
771     { &hf_pgm_opt_tlen,
772       { "Total Length", "pgm.opts.tlen", FT_UINT16, BASE_DEC, NULL, 0x0, 
773                 "", HFILL }},
774     { &hf_pgm_genopt_type,
775       { "Type", "pgm.genopts.type", FT_UINT8, BASE_HEX, NULL, 0x0, 
776                 "", HFILL }},
777     { &hf_pgm_genopt_len,
778       { "Length", "pgm.genopts.len", FT_UINT8, BASE_DEC, NULL, 0x0, 
779                 "", HFILL }},
780     { &hf_pgm_genopt_opx,
781       { "Option Extensibility Bits", "pgm.genopts.opx", FT_UINT8, 
782                   BASE_HEX, NULL, 0x0, "", HFILL }},
783     { &hf_pgm_genopt_res,
784       { "Reserved", "pgm.genopts.opx", FT_UINT8, BASE_HEX, NULL, 0x0, 
785                 "", HFILL }},
786     { &hf_pgm_opt_parity_prm_type,
787       { "Type", "pgm.parity_prm.type", FT_UINT8, BASE_HEX, NULL, 0x0, 
788                 "", HFILL }},
789     { &hf_pgm_opt_parity_prm_len,
790       { "Length", "pgm.parity_prm.len", FT_UINT8, BASE_DEC, NULL, 0x0, 
791                 "", HFILL }},
792     { &hf_pgm_opt_parity_prm_opx,
793       { "Option Extensibility Bits", "pgm.opts.parity_prm.opx", FT_UINT8, 
794                   BASE_HEX, NULL, 0x0, "", HFILL }},
795     { &hf_pgm_opt_parity_prm_po,
796       { "Pro-Active Parity", "pgm.opts.parity_prm.op", FT_UINT8, 
797                   BASE_HEX, NULL, 0x0, "", HFILL }},
798     { &hf_pgm_opt_parity_prm_prmtgsz,
799       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp", FT_UINT32, 
800                   BASE_HEX, NULL, 0x0, "", HFILL }},
801     { &hf_pgm_opt_join_type,
802       { "Type", "pgm.opts.join.type", FT_UINT8, 
803                   BASE_HEX, NULL, 0x0, "", HFILL }},
804     { &hf_pgm_opt_join_len,
805       { "Length", "pgm.opts.join.res", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
806     { &hf_pgm_opt_join_opx,
807       { "Option Extensibility Bits", "pgm.opts.join.opx", FT_UINT8, 
808                   BASE_HEX, NULL, 0x0, "", HFILL }},
809     { &hf_pgm_opt_join_res,
810       { "Reserved", "pgm.opts.join.res", FT_UINT8, 
811                   BASE_HEX, NULL, 0x0, "", HFILL }},
812     { &hf_pgm_opt_join_minjoin,
813       { "Minimum Sequence Number", "pgm.opts.join.min_join", FT_UINT32, 
814                   BASE_HEX, NULL, 0x0, "", HFILL }},
815     { &hf_pgm_opt_parity_grp_type,
816       { "Type", "pgm.parity_grp.type", FT_UINT8, BASE_HEX, NULL, 0x0, 
817                 "", HFILL }},
818     { &hf_pgm_opt_parity_grp_len,
819       { "Length", "pgm.parity_grp.len", FT_UINT8, BASE_DEC, NULL, 0x0, 
820                 "", HFILL }},
821     { &hf_pgm_opt_parity_grp_opx,
822       { "Option Extensibility Bits", "pgm.opts.parity_prm.opx", FT_UINT8, 
823                   BASE_HEX, NULL, 0x0, "", HFILL }},
824     { &hf_pgm_opt_parity_grp_res,
825       { "Reserved", "pgm.opts.parity_prm.op", FT_UINT8, 
826                   BASE_HEX, NULL, 0x0, "", HFILL }},
827     { &hf_pgm_opt_parity_grp_prmgrp,
828       { "Transmission Group Size", "pgm.opts.parity_prm.prm_grp", FT_UINT32, 
829                   BASE_HEX, NULL, 0x0, "", HFILL }},
830     { &hf_pgm_opt_nak_type,
831       { "Type", "pgm.opt.nak.type", FT_UINT8, BASE_HEX, NULL, 0x0, 
832                 "", HFILL }},
833     { &hf_pgm_opt_nak_len,
834       { "Length", "pgm.opts.nak.len", FT_UINT8, BASE_DEC, NULL, 0x0, 
835                 "", HFILL }},
836     { &hf_pgm_opt_nak_opx,
837       { "Option Extensibility Bits", "pgm.opts.nak.opx", FT_UINT8, 
838                   BASE_HEX, NULL, 0x0, "", HFILL }},
839     { &hf_pgm_opt_nak_res,
840       { "Reserved", "pgm.opts.nak.op", FT_UINT8, 
841                   BASE_HEX, NULL, 0x0, "", HFILL }},
842     { &hf_pgm_opt_nak_list,
843       { "List", "pgm.opts.nak.list", FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL }},
844   };
845   static gint *ett[] = {
846     &ett_pgm,
847         &ett_pgm_optbits,
848         &ett_pgm_spm,
849         &ett_pgm_data,
850         &ett_pgm_nak,
851         &ett_pgm_opts,
852         &ett_pgm_opts_join,
853         &ett_pgm_opts_parityprm,
854         &ett_pgm_opts_paritygrp,
855         &ett_pgm_opts_naklist,
856   };
857
858   proto_pgm = proto_register_protocol("Pragmatic General Multicast",
859                                        "PGM", "pgm");
860
861   proto_register_field_array(proto_pgm, hf, array_length(hf));
862   proto_register_subtree_array(ett, array_length(ett));
863
864 }
865
866 /* The registration hand-off routine */
867 void
868 proto_reg_handoff_pgm(void)
869 {
870   dissector_add("ip.proto", IP_PROTO_PGM, dissect_pgm, proto_pgm);
871
872 }