2 * Routines for pgm packet disassembly
4 * $Id: packet-pgm.c,v 1.10 2001/12/03 03:59:37 guy Exp $
6 * Copyright (c) 2000 by Talarian Corp
8 * Ethereal - Network traffic analyzer
9 * By Gerald Combs <gerald@ethereal.com>
10 * Copyright 1999 Gerald Combs
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.
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.
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.
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
35 #ifdef HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
49 #include "packet-pgm.h"
54 #include "conversation.h"
59 void proto_reg_handoff_pgm(void);
60 static void proto_rereg_pgm(void);
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;
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;
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;
110 static int hf_pgm_opt_type = -1;
111 static int hf_pgm_opt_len = -1;
112 static int hf_pgm_opt_tlen = -1;
114 static int hf_pgm_genopt_type = -1;
115 static int hf_pgm_genopt_len = -1;
116 static int hf_pgm_genopt_opx = -1;
118 static int hf_pgm_opt_join_res = -1;
119 static int hf_pgm_opt_join_minjoin = -1;
121 static int hf_pgm_opt_parity_prm_po = -1;
122 static int hf_pgm_opt_parity_prm_prmtgsz = -1;
124 static int hf_pgm_opt_parity_grp_res = -1;
125 static int hf_pgm_opt_parity_grp_prmgrp = -1;
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;
133 static int hf_pgm_opt_nak_res = -1;
134 static int hf_pgm_opt_nak_list = -1;
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;
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;
148 static dissector_table_t subdissector_table;
149 static heur_dissector_list_t heur_subdissector_list;
150 static dissector_handle_t data_handle;
153 * As of the time this comment was typed
155 * http://search.ietf.org/internet-drafts/draft-speakman-pgm-spec-06.txt
157 * was the URL for the PGM draft.
161 optsstr(nchar_t opts)
163 static char msg[256];
170 sprintf(p, "Present");
171 p += strlen("Present");
173 if (opts & PGM_OPT_NETSIG){
181 if (opts & PGM_OPT_VAR_PKTLEN){
189 if (opts & PGM_OPT_PARITY){
198 sprintf(p, "0x%x", opts);
203 paritystr(nchar_t parity)
205 static char msg[256];
211 if (parity & PGM_OPT_PARITY_PRM_PRO){
212 sprintf(p, "Pro-active");
213 p += strlen("Pro-active");
215 if (parity & PGM_OPT_PARITY_PRM_OND){
224 sprintf(p, "0x%x", parity);
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" },
247 static const value_string opx_vals[] = {
248 { PGM_OPX_IGNORE, "Ignore" },
249 { PGM_OPX_INVAL, "Inval" },
250 { PGM_OPX_DISCARD, "DisCard" },
255 dissect_pgmopts(tvbuff_t *tvb, int offset, proto_tree *tree,
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;
265 tvb_memcpy(tvb, (guint8 *)&opts, offset, sizeof(opts));
266 opts.total_len = ntohs(opts.total_len);
268 tf = proto_tree_add_text(tree, tvb, offset,
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);
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;
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)"),
290 if (genopts.len == 0)
293 switch(genopts.type) {
295 pgm_opt_join_t optdata;
297 tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
298 opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_join);
300 proto_tree_add_uint(opt_tree, hf_pgm_genopt_type,
301 tvb, offset, 1, genopts.type);
303 proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb,
304 offset+1, 1, genopts.len);
306 proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx, tvb,
307 offset+2, 1, genopts.opx);
309 proto_tree_add_uint(opt_tree, hf_pgm_opt_join_res, tvb,
310 offset+3, 1, optdata.res);
312 proto_tree_add_uint(opt_tree, hf_pgm_opt_join_minjoin, tvb,
313 offset+4, 4, ntohl(optdata.opt_join_min));
317 case PGM_OPT_PARITY_PRM:{
318 pgm_opt_parity_prm_t optdata;
320 tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
321 opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_parityprm);
323 proto_tree_add_uint(opt_tree, hf_pgm_genopt_type,
324 tvb, offset, 1, genopts.type);
326 proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb,
327 offset+1, 1, genopts.len);
329 proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx,
330 tvb, offset+2, 1, genopts.opx);
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);
336 proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_prm_prmtgsz,
337 tvb, offset+4, 4, ntohl(optdata.prm_tgsz));
341 case PGM_OPT_PARITY_GRP:{
342 pgm_opt_parity_grp_t optdata;
344 tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
345 opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_paritygrp);
347 proto_tree_add_uint(opt_tree, hf_pgm_genopt_type,
348 tvb, offset, 1, genopts.type);
350 proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb,
351 offset+1, 1, genopts.len);
353 proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx,
354 tvb, offset+2, 1, genopts.opx);
356 proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_grp_res, tvb,
357 offset+3, 1, optdata.res);
359 proto_tree_add_uint(opt_tree, hf_pgm_opt_parity_grp_prmgrp,
360 tvb, offset+4, 4, ntohl(optdata.prm_grp));
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;
370 tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
371 opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_naklist);
373 proto_tree_add_uint(opt_tree, hf_pgm_genopt_type, tvb,
374 offset, 1, genopts.type);
376 proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb,
377 offset+1, 1, genopts.len);
379 proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx,
380 tvb, offset+2, 1, genopts.opx);
382 proto_tree_add_uint(opt_tree, hf_pgm_opt_nak_res, tvb,
383 offset+3, 1, optdata.res);
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));
391 * Print out 8 per line
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) {
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);
404 proto_tree_add_bytes_format(opt_tree,
405 hf_pgm_opt_nak_list, tvb, offset+4, optdata.len,
406 nakbuf, "List: %s", nakbuf);
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);
419 proto_tree_add_bytes_format(opt_tree,
420 hf_pgm_opt_nak_list, tvb, offset+4, optdata.len,
421 nakbuf, "List: %s", nakbuf);
427 case PGM_OPT_PGMCC_DATA:{
428 pgm_opt_pgmcc_data_t optdata;
430 tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
431 opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_ccdata);
433 proto_tree_add_uint(opt_tree, hf_pgm_genopt_type,
434 tvb, offset, 1, genopts.type);
436 proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb,
437 offset+1, 1, genopts.len);
439 proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx,
440 tvb, offset+2, 1, genopts.opx);
442 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_res, tvb,
443 offset+3, 1, optdata.res);
445 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_tsp, tvb,
446 offset+4, 4, optdata.tsp);
448 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_afi, tvb,
449 offset+8, 2, ntohs(optdata.acker_afi));
451 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccdata_res2, tvb,
452 offset+10, 2, ntohs(optdata.res2));
454 switch (ntohs(optdata.acker_afi)) {
457 proto_tree_add_ipv4(opt_tree, hf_pgm_opt_ccdata_acker,
458 tvb, offset+12, 4, optdata.acker);
463 * XXX - the header is variable-length,
464 * as the length of the NLA depends on
467 * However, our structure for it is
468 * fixed-length, and assumes it's a 4-byte
476 case PGM_OPT_PGMCC_FEEDBACK:{
477 pgm_opt_pgmcc_feedback_t optdata;
479 tvb_memcpy(tvb, (guint8 *)&optdata, offset, sizeof(optdata));
480 opt_tree = proto_item_add_subtree(tf, ett_pgm_opts_ccdata);
482 proto_tree_add_uint(opt_tree, hf_pgm_genopt_type,
483 tvb, offset, 1, genopts.type);
485 proto_tree_add_uint(opt_tree, hf_pgm_genopt_len, tvb,
486 offset+1, 1, genopts.len);
488 proto_tree_add_uint(opt_tree, hf_pgm_genopt_opx,
489 tvb, offset+2, 1, genopts.opx);
491 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_res, tvb,
492 offset+3, 1, optdata.res);
494 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_tsp, tvb,
495 offset+4, 4, optdata.tsp);
497 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_afi, tvb,
498 offset+8, 2, ntohs(optdata.acker_afi));
500 proto_tree_add_uint(opt_tree, hf_pgm_opt_ccfeedbk_lossrate, tvb,
501 offset+10, 2, ntohs(optdata.loss_rate));
503 switch (ntohs(optdata.acker_afi)) {
506 proto_tree_add_ipv4(opt_tree, hf_pgm_opt_ccfeedbk_acker,
507 tvb, offset+12, 4, optdata.acker);
512 * XXX - the header is variable-length,
513 * as the length of the NLA depends on
516 * However, our structure for it is
517 * fixed-length, and assumes it's a 4-byte
526 offset += genopts.len;
527 opts.total_len -= genopts.len;
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" },
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 */
548 decode_pgm_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
549 proto_tree *tree, pgm_type *pgmhdr)
554 next_tvb = tvb_new_subset(tvb, offset, -1, -1);
556 /* do lookup with the subdissector table */
557 found = dissector_try_port(subdissector_table, pgmhdr->sport,
558 next_tvb, pinfo, tree);
562 found = dissector_try_port(subdissector_table, pgmhdr->dport,
563 next_tvb, pinfo, tree);
567 /* do lookup with the heuristic subdissector table */
568 if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
571 /* Oh, well, we don't know this; dissect it as data. */
572 call_dissector(data_handle,next_tvb, pinfo, tree);
576 total_size(tvbuff_t *tvb, pgm_type *hdr)
578 int bytes = sizeof(pgm_type);
579 pgm_opt_length_t opts;
583 bytes += sizeof(pgm_spm_t);
588 bytes += sizeof(pgm_data_t);
594 bytes += sizeof(pgm_nak_t);
597 bytes += sizeof(pgm_ack_t);
600 if ((hdr->opts & PGM_OPT)) {
601 tvb_memcpy(tvb, (guint8 *)&opts, bytes, sizeof(opts));
602 bytes += ntohs(opts.total_len);
607 * dissect_pgm - The dissector for Pragmatic General Multicast
610 dissect_pgm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
612 proto_tree *pgm_tree = NULL;
613 proto_tree *opt_tree = NULL;
614 proto_tree *type_tree = NULL;
628 if (check_col(pinfo->fd, COL_PROTOCOL))
629 col_set_str(pinfo->fd, COL_PROTOCOL, "PGM");
631 /* Clear out the Info column. */
632 if (check_col(pinfo->fd, COL_INFO))
633 col_clear(pinfo->fd, COL_INFO);
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);
641 pktname = val_to_str(pgmhdr.type, type_vals, "Unknown (0x%02x)");
643 gsi = bytes_to_str(pgmhdr.gsi, 6);
644 switch(pgmhdr.type) {
646 plen = sizeof(pgm_spm_t);
647 tvb_memcpy(tvb, (guint8 *)&spm, sizeof(pgm_type), plen);
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);
657 plen = sizeof(pgm_data_t);
658 tvb_memcpy(tvb, (guint8 *)&data, sizeof(pgm_type), plen);
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,
671 plen = sizeof(pgm_nak_t);
672 tvb_memcpy(tvb, (guint8 *)&nak, sizeof(pgm_type), plen);
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);
680 plen = sizeof(pgm_ack_t);
681 tvb_memcpy(tvb, (guint8 *)&ack, sizeof(pgm_type), plen);
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);
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));
701 pgm_tree = proto_item_add_subtree(ti, ett_pgm);
702 proto_tree_add_uint(pgm_tree, hf_pgm_main_sport, tvb, offset, 2,
704 proto_tree_add_uint(pgm_tree, hf_pgm_main_dport, tvb, offset+2,
706 proto_tree_add_uint(pgm_tree, hf_pgm_main_type, tvb,
707 offset+4, 1, pgmhdr.type);
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);
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));
723 proto_tree_add_uint(pgm_tree, hf_pgm_main_cksum, tvb, offset+6,
725 proto_tree_add_bytes(pgm_tree, hf_pgm_main_gsi, tvb, offset+8,
727 proto_tree_add_uint(pgm_tree, hf_pgm_main_tsdulen, tvb,
728 offset+14, 2, pgmhdr.tsdulen);
730 offset = sizeof(pgm_type);
731 tf = proto_tree_add_text(pgm_tree, tvb, offset, plen, "%s Packet",
733 switch(pgmhdr.type) {
735 type_tree = proto_item_add_subtree(tf, ett_pgm_spm);
737 proto_tree_add_uint(type_tree, hf_pgm_spm_sqn, tvb,
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) {
750 proto_tree_add_ipv4(type_tree, hf_pgm_spm_path,
751 tvb, offset+14, 4, spm.path);
756 * XXX - the header is variable-length,
757 * as the length of the NLA depends on
760 * However, our structure for it is
761 * fixed-length, and assumes it's a 4-byte
767 if ((pgmhdr.opts & PGM_OPT) == FALSE)
771 dissect_pgmopts(tvb, offset, type_tree, pktname);
776 case PGM_ODATA_PCKT: {
777 type_tree = proto_item_add_subtree(tf, ett_pgm_data);
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);
784 if ((pgmhdr.opts & PGM_OPT) == FALSE)
788 dissect_pgmopts(tvb, offset, type_tree, pktname);
797 type_tree = proto_item_add_subtree(tf, ett_pgm_nak);
799 proto_tree_add_uint(type_tree, hf_pgm_nak_sqn, tvb,
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);
806 switch (nak.src_afi) {
809 proto_tree_add_ipv4(type_tree, hf_pgm_nak_src,
810 tvb, offset+8, 4, nak.src);
815 * XXX - the header is variable-length,
816 * as the length of the NLA depends on
819 * However, our structure for it is
820 * fixed-length, and assumes it's a 4-byte
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);
831 switch (nak.grp_afi) {
834 proto_tree_add_ipv4(type_tree, hf_pgm_nak_grp,
835 tvb, offset+16, 4, nak.grp);
840 * XXX - the header is variable-length,
841 * as the length of the NLA depends on
844 * However, our structure for it is
845 * fixed-length, and assumes it's a 4-byte
851 if ((pgmhdr.opts & PGM_OPT) == FALSE)
855 dissect_pgmopts(tvb, offset, type_tree, pktname);
859 type_tree = proto_item_add_subtree(tf, ett_pgm_ack);
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);
866 if ((pgmhdr.opts & PGM_OPT) == FALSE)
870 dissect_pgmopts(tvb, offset, type_tree, pktname);
878 * Now see if there are any sub-dissectors, if so call them
880 offset = total_size(tvb, &pgmhdr);
881 decode_pgm_ports(tvb, offset, pinfo, tree, &pgmhdr);
885 static const true_false_string opts_present = {
890 /* Register all the bits needed with the filtering engine */
892 proto_register_pgm(void)
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 }},
902 { "Type", "pgm.hdr.type", FT_UINT8, BASE_HEX,
903 VALS(type_vals), 0x0, "", HFILL }},
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 }},
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 }},
931 { "Sequence number", "pgm.spm.sqn", FT_UINT32, BASE_HEX,
932 NULL, 0x0, "", HFILL }},
934 { "Trailing Edge Sequence Number", "pgm.spm.trail", FT_UINT32, BASE_HEX,
935 NULL, 0x0, "", HFILL }},
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 }},
943 { "Reserved", "pgm.spm.res", FT_UINT16, BASE_HEX,
944 NULL, 0x0, "", HFILL }},
946 { "Path NLA", "pgm.spm.path", FT_IPv4, BASE_NONE,
947 NULL, 0x0, "", HFILL }},
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 }},
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 }},
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 }},
973 { "Multicast Group NLA", "pgm.nak.grp", FT_IPv4, BASE_NONE,
974 NULL, 0x0, "", HFILL }},
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 }},
982 { "Type", "pgm.opts.type", FT_UINT8, BASE_HEX,
983 VALS(opt_vals), 0x0, "", HFILL }},
985 { "Length", "pgm.opts.len", FT_UINT8, BASE_DEC,
986 NULL, 0x0, "", HFILL }},
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 }},
1056 static gint *ett[] = {
1065 &ett_pgm_opts_parityprm,
1066 &ett_pgm_opts_paritygrp,
1067 &ett_pgm_opts_naklist,
1068 &ett_pgm_opts_ccdata,
1070 module_t *pgm_module;
1072 proto_pgm = proto_register_protocol("Pragmatic General Multicast",
1075 proto_register_field_array(proto_pgm, hf, array_length(hf));
1076 proto_register_subtree_array(ett, array_length(ett));
1078 /* subdissector code */
1079 subdissector_table = register_dissector_table("pgm.port");
1080 register_heur_dissector_list("pgm", &heur_subdissector_list);
1083 * Register configuration preferences for UDP encapsulation
1084 * (Note: Initially the ports are set to zero so the
1085 * dissecting of PGM encapsulated in UPD packets
1086 * is off by default)
1088 pgm_module = prefs_register_protocol(proto_pgm, proto_rereg_pgm);
1090 prefs_register_uint_preference(pgm_module, "udp.encap_ucast_port",
1091 "PGM Encap Unicast Port (Default 3055)",
1092 "PGM Encap is PGM packets encapsulated in UDP packets"
1093 " (Note: This is option is off by default",
1094 10, &udp_encap_ucast_port);
1095 old_encap_ucast_port = udp_encap_ucast_port;
1097 prefs_register_uint_preference(pgm_module, "udp.encap_mcast_port",
1098 "PGM Encap Multicast Port (Default 3056)",
1099 "PGM Encap is PGM packets encapsulated in UDP packets"
1100 " (Note: This is option is off by default",
1101 10, &udp_encap_mcast_port);
1103 old_encap_mcast_port = udp_encap_mcast_port;
1106 static dissector_handle_t pgm_handle;
1108 /* The registration hand-off routine */
1110 proto_reg_handoff_pgm(void)
1112 pgm_handle = create_dissector_handle(dissect_pgm, proto_pgm);
1115 * Set up PGM Encap dissecting, which is off by default
1117 dissector_add("udp.port", udp_encap_ucast_port, pgm_handle);
1118 dissector_add("udp.port", udp_encap_mcast_port, pgm_handle);
1120 dissector_add("ip.proto", IP_PROTO_PGM, pgm_handle);
1122 data_handle = find_dissector("data");
1126 proto_rereg_pgm(void)
1129 * Remove the old ones
1131 dissector_delete("udp.port", old_encap_ucast_port, pgm_handle);
1132 dissector_delete("udp.port", old_encap_mcast_port, pgm_handle);
1137 dissector_add("udp.port", udp_encap_ucast_port, pgm_handle);
1138 dissector_add("udp.port", udp_encap_mcast_port, pgm_handle);