/* * dissect_pgm - The dissector for Pragmatic General Multicast */ static void dissect_pgm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { guint16 pgmhdr_sport; guint16 pgmhdr_dport; guint8 pgmhdr_type; guint8 pgmhdr_opts; guint16 pgmhdr_cksum; guint16 pgmhdr_tsdulen; guint32 sqn; guint16 afi; guint plen = 0; proto_item *ti; const char *pktname; const char *pollstname; char *gsi; gboolean isdata = FALSE; guint pgmlen, reportedlen; col_set_str(pinfo->cinfo, COL_PROTOCOL, "PGM"); if (check_col(pinfo->cinfo, COL_INFO)) { col_clear(pinfo->cinfo, COL_INFO); if (tvb_reported_length_remaining(tvb, 0) < 18) { col_set_str(pinfo->cinfo, COL_INFO, "Packet too small"); return; } } pinfo->srcport = pgmhdr_sport = tvb_get_ntohs(tvb, 0); pinfo->destport = pgmhdr_dport = tvb_get_ntohs(tvb, 2); pgmhdr_type = tvb_get_guint8(tvb, 4); pktname = val_to_str(pgmhdr_type, type_vals, "Unknown (0x%02x)"); pgmhdr_opts = tvb_get_guint8(tvb, 5); pgmhdr_cksum = tvb_get_ntohs(tvb, 6); gsi = tvb_bytes_to_str(tvb, 8, 6); pgmhdr_tsdulen = tvb_get_ntohs(tvb, 14); sqn = tvb_get_ntohl(tvb, 16); switch(pgmhdr_type) { case PGM_SPM_PCKT: case PGM_NAK_PCKT: case PGM_NNAK_PCKT: case PGM_NCF_PCKT: case PGM_POLR_PCKT: case PGM_ACK_PCKT: if (check_col(pinfo->cinfo, COL_INFO)) { col_add_fstr(pinfo->cinfo, COL_INFO, "%-5s sqn 0x%x gsi %s", pktname, sqn, gsi); } break; case PGM_RDATA_PCKT: case PGM_ODATA_PCKT: if (check_col(pinfo->cinfo, COL_INFO)) { col_add_fstr(pinfo->cinfo, COL_INFO, "%-5s sqn 0x%x gsi %s tsdulen %d", pktname, sqn, gsi, pgmhdr_tsdulen); } isdata = TRUE; break; case PGM_POLL_PCKT: { guint16 poll_stype = tvb_get_ntohs(tvb, 22); pollstname = val_to_str(poll_stype, poll_subtype_vals, "Unknown (0x%02x)"); if (check_col(pinfo->cinfo, COL_INFO)) { col_add_fstr(pinfo->cinfo, COL_INFO, "%-5s sqn 0x%x gsi %s subtype %s", pktname, sqn, gsi, pollstname); } } break; default: return; } { proto_tree *pgm_tree = NULL; proto_tree *opt_tree = NULL; proto_tree *type_tree = NULL; proto_item *tf, *hidden_item; ptvcursor_t* cursor; ti = proto_tree_add_protocol_format(tree, proto_pgm, tvb, 0, -1, "Pragmatic General Multicast: Type %s" " Src Port %u, Dst Port %u, GSI %s", pktname, pgmhdr_sport, pgmhdr_dport, gsi); pgm_tree = proto_item_add_subtree(ti, ett_pgm); cursor = ptvcursor_new(pgm_tree, tvb, 0); hidden_item = proto_tree_add_item(pgm_tree, hf_pgm_port, tvb, 0, 2, ENC_BIG_ENDIAN); PROTO_ITEM_SET_HIDDEN(hidden_item); hidden_item = proto_tree_add_item(pgm_tree, hf_pgm_port, tvb, 2, 2, ENC_BIG_ENDIAN); PROTO_ITEM_SET_HIDDEN(hidden_item); ptvcursor_add(cursor, hf_pgm_main_sport, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_main_dport, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_main_type, 1, ENC_BIG_ENDIAN); tf = proto_tree_add_uint_format(pgm_tree, hf_pgm_main_opts, tvb, ptvcursor_current_offset(cursor), 1, pgmhdr_opts, "Options: %s (0x%x)", optsstr(pgmhdr_opts), pgmhdr_opts); opt_tree = proto_item_add_subtree(tf, ett_pgm_optbits); ptvcursor_set_tree(cursor, opt_tree); ptvcursor_add_no_advance(cursor, hf_pgm_main_opts_opt, 1, ENC_BIG_ENDIAN); ptvcursor_add_no_advance(cursor, hf_pgm_main_opts_netsig, 1, ENC_BIG_ENDIAN); ptvcursor_add_no_advance(cursor, hf_pgm_main_opts_varlen, 1, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_main_opts_parity, 1, ENC_BIG_ENDIAN); ptvcursor_set_tree(cursor, pgm_tree); /* Checksum may be 0 (not available), but not for DATA packets */ if ((pgmhdr_type != PGM_RDATA_PCKT) && (pgmhdr_type != PGM_ODATA_PCKT) && (pgmhdr_cksum == 0)) { proto_tree_add_uint_format(pgm_tree, hf_pgm_main_cksum, tvb, ptvcursor_current_offset(cursor), 2, pgmhdr_cksum, "Checksum: not available"); } else { reportedlen = tvb_reported_length(tvb); pgmlen = tvb_length(tvb); if (pgm_check_checksum && pgmlen >= reportedlen) { vec_t cksum_vec[1]; guint16 computed_cksum; cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pgmlen); cksum_vec[0].len = pgmlen; computed_cksum = in_cksum(&cksum_vec[0], 1); if (computed_cksum == 0) { proto_tree_add_uint_format(pgm_tree, hf_pgm_main_cksum, tvb, ptvcursor_current_offset(cursor), 2, pgmhdr_cksum, "Checksum: 0x%04x [correct]", pgmhdr_cksum); } else { hidden_item = proto_tree_add_boolean(pgm_tree, hf_pgm_main_cksum_bad, tvb, ptvcursor_current_offset(cursor), 2, TRUE); PROTO_ITEM_SET_HIDDEN(hidden_item); proto_tree_add_uint_format(pgm_tree, hf_pgm_main_cksum, tvb, ptvcursor_current_offset(cursor), 2, pgmhdr_cksum, "Checksum: 0x%04x [incorrect, should be 0x%04x]", pgmhdr_cksum, in_cksum_shouldbe(pgmhdr_cksum, computed_cksum)); } } else { ptvcursor_add_no_advance(cursor, hf_pgm_main_cksum, 2, ENC_BIG_ENDIAN); } } ptvcursor_advance(cursor, 2); ptvcursor_add(cursor, hf_pgm_main_gsi, 6, ENC_NA); ptvcursor_add(cursor, hf_pgm_main_tsdulen, 2, ENC_BIG_ENDIAN); tf = proto_tree_add_text(pgm_tree, tvb, ptvcursor_current_offset(cursor), plen, "%s Packet", pktname); switch(pgmhdr_type) { case PGM_SPM_PCKT: type_tree = proto_item_add_subtree(tf, ett_pgm_spm); ptvcursor_set_tree(cursor, type_tree); ptvcursor_add(cursor, hf_pgm_spm_sqn, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_spm_trail, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_spm_lead, 4, ENC_BIG_ENDIAN); afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor)); ptvcursor_add(cursor, hf_pgm_spm_pathafi, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_spm_res, 2, ENC_BIG_ENDIAN); switch (afi) { case AFNUM_INET: ptvcursor_add(cursor, hf_pgm_spm_path, 4, ENC_BIG_ENDIAN); break; case AFNUM_INET6: ptvcursor_add(cursor, hf_pgm_spm_path6, 16, ENC_NA); break; default: proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, "Can't handle this address format"); return; } break; case PGM_RDATA_PCKT: case PGM_ODATA_PCKT: type_tree = proto_item_add_subtree(tf, ett_pgm_data); ptvcursor_set_tree(cursor, type_tree); ptvcursor_add(cursor, hf_pgm_spm_sqn, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_spm_trail, 4, ENC_BIG_ENDIAN); break; case PGM_NAK_PCKT: case PGM_NNAK_PCKT: case PGM_NCF_PCKT: type_tree = proto_item_add_subtree(tf, ett_pgm_nak); ptvcursor_set_tree(cursor, type_tree); ptvcursor_add(cursor, hf_pgm_nak_sqn, 4, ENC_BIG_ENDIAN); afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor)); ptvcursor_add(cursor, hf_pgm_nak_srcafi, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_nak_srcres, 2, ENC_BIG_ENDIAN); switch (afi) { case AFNUM_INET: ptvcursor_add(cursor, hf_pgm_nak_src, 4, ENC_BIG_ENDIAN); break; case AFNUM_INET6: ptvcursor_add(cursor, hf_pgm_nak_src6, 16, ENC_NA); break; default: proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, "Can't handle this address format"); break; } afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor)); ptvcursor_add(cursor, hf_pgm_nak_grpafi, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_nak_grpres, 2, ENC_BIG_ENDIAN); switch (afi) { case AFNUM_INET: ptvcursor_add(cursor, hf_pgm_nak_grp, 4, ENC_BIG_ENDIAN); break; case AFNUM_INET6: ptvcursor_add(cursor, hf_pgm_nak_grp6, 16, ENC_NA); break; default: proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, "Can't handle this address format"); return; } break; case PGM_POLL_PCKT: type_tree = proto_item_add_subtree(tf, ett_pgm_poll); ptvcursor_set_tree(cursor, type_tree); ptvcursor_add(cursor, hf_pgm_poll_sqn, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_poll_round, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_poll_subtype, 2, ENC_BIG_ENDIAN); afi = tvb_get_ntohs(tvb, ptvcursor_current_offset(cursor)); ptvcursor_add(cursor, hf_pgm_poll_pathafi, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_poll_res, 2, ENC_BIG_ENDIAN); switch (afi) { case AFNUM_INET: ptvcursor_add(cursor, hf_pgm_poll_path, 4, ENC_BIG_ENDIAN); break; case AFNUM_INET6: ptvcursor_add(cursor, hf_pgm_poll_path6, 16, ENC_NA); break; default: proto_tree_add_text(type_tree, tvb, ptvcursor_current_offset(cursor), -1, "Can't handle this address format"); break; } ptvcursor_add(cursor, hf_pgm_poll_backoff_ivl, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_poll_rand_str, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_poll_matching_bmask, 4, ENC_BIG_ENDIAN); break; case PGM_POLR_PCKT: type_tree = proto_item_add_subtree(tf, ett_pgm_polr); ptvcursor_set_tree(cursor, type_tree); ptvcursor_add(cursor, hf_pgm_polr_sqn, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_polr_round, 2, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_polr_res, 2, ENC_BIG_ENDIAN); break; case PGM_ACK_PCKT: type_tree = proto_item_add_subtree(tf, ett_pgm_ack); ptvcursor_set_tree(cursor, type_tree); ptvcursor_add(cursor, hf_pgm_ack_sqn, 4, ENC_BIG_ENDIAN); ptvcursor_add(cursor, hf_pgm_ack_bitmap, 4, ENC_BIG_ENDIAN); break; } if (pgmhdr_opts & PGM_OPT) dissect_pgmopts(cursor, pktname); if (isdata) decode_pgm_ports(tvb, ptvcursor_current_offset(cursor), pinfo, tree, pgmhdr_sport, pgmhdr_dport); } }
/* * For PIM v2, see RFC 4601, RFC 3973 and draft-ietf-pim-sm-v2-new-03 * (when PIM is run over IPv6, the rules for computing the PIM checksum * from the draft in question, not from RFC 2362, should be used). */ static void dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { int offset = 0; guint8 pim_typever; guint length, pim_length; guint16 pim_cksum, computed_cksum; vec_t cksum_vec[4]; guint32 phdr[2]; const char *typestr; proto_tree *pim_tree = NULL; proto_item *ti; proto_tree *pimopt_tree = NULL; proto_item *tiopt; col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIM"); col_clear(pinfo->cinfo, COL_INFO); pim_typever = tvb_get_guint8(tvb, 0); switch (PIM_VER(pim_typever)) { case 2: typestr = val_to_str(PIM_TYPE(pim_typever), type2vals, "Unknown (%u)"); break; case 1: /* PIMv1 - we should never see this */ default: typestr = "Unknown"; break; } if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "PIMv%d", PIM_VER(pim_typever)); } if (check_col(pinfo->cinfo, COL_INFO)) col_add_str(pinfo->cinfo, COL_INFO, typestr); ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, ENC_NA); pim_tree = proto_item_add_subtree(ti, ett_pim); proto_tree_add_item(pim_tree, hf_pim_version, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(pim_tree, hf_pim_type, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(pim_tree, hf_pim_res_bytes, tvb, offset + 1, 1, ENC_NA); pim_cksum = tvb_get_ntohs(tvb, offset + 2); length = tvb_length(tvb); if (PIM_VER(pim_typever) == 2) { /* * Well, it's PIM v2, so we can check whether this is a Register * message, and thus can figure out how much to checksum and * whether to make the columns read-only. */ if (PIM_TYPE(pim_typever) == 1) { /* * Register message - the PIM header is 8 bytes long. * Also set the columns non-writable. Otherwise the IPv4 or * IPv6 dissector for the encapsulated packet that caused * this register will overwrite the PIM info in the columns. */ pim_length = 8; col_set_writable(pinfo->cinfo, FALSE); } else { /* * Other message - checksum the entire packet. */ pim_length = tvb_reported_length(tvb); } } else { /* * We don't know what type of message this is, so say that * the length is 0, to force it not to be checksummed. */ pim_length = 0; } if (!pinfo->fragmented && length >= pim_length) { /* * The packet isn't part of a fragmented datagram and isn't * truncated, so we can checksum it. */ switch (pinfo->src.type) { case AT_IPv4: cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length); cksum_vec[0].len = pim_length; computed_cksum = in_cksum(&cksum_vec[0], 1); break; case AT_IPv6: /* Set up the fields of the pseudo-header. */ cksum_vec[0].ptr = pinfo->src.data; cksum_vec[0].len = pinfo->src.len; cksum_vec[1].ptr = pinfo->dst.data; cksum_vec[1].len = pinfo->dst.len; cksum_vec[2].ptr = (const guint8 *)&phdr; phdr[0] = g_htonl(pim_length); phdr[1] = g_htonl(IP_PROTO_PIM); cksum_vec[2].len = 8; cksum_vec[3].ptr = tvb_get_ptr(tvb, 0, pim_length); cksum_vec[3].len = pim_length; computed_cksum = in_cksum(&cksum_vec[0], 4); break; default: /* PIM is available for IPv4 and IPv6 right now */ computed_cksum = 0; /* squelch GCC complaints */ DISSECTOR_ASSERT_NOT_REACHED(); break; } if (computed_cksum == 0) { proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb, offset + 2, 2, pim_cksum, "Checksum: 0x%04x [correct]", pim_cksum); } else { proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb, offset + 2, 2, pim_cksum, "Checksum: 0x%04x [incorrect, should be 0x%04x]", pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum)); } } else { proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb, offset + 2, 2, pim_cksum); } offset += 4; if (tvb_reported_length_remaining(tvb, offset) > 0) { tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1, "PIM options"); pimopt_tree = proto_item_add_subtree(tiopt, ett_pim_opts); } else goto done; if (PIM_VER(pim_typever) != 2) goto done; /* version 2 decoder */ switch (PIM_TYPE(pim_typever)) { case 0: /*hello*/ { int opt_count = 0; while (tvb_reported_length_remaining(tvb, offset) >= 2) { guint16 hello_opt, opt_len; guint16 opt_value; proto_item *opt_item; proto_tree *opt_tree; opt_count++; hello_opt = tvb_get_ntohs(tvb, offset); opt_len = tvb_get_ntohs(tvb, offset + 2); opt_item = proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len, "Option %u: %s", hello_opt, val_to_str(hello_opt, pim_opt_vals, "Unknown: %u")); opt_tree = proto_item_add_subtree(opt_item, ett_pim_opt); proto_tree_add_item(opt_tree, hf_pim_optiontype, tvb, offset, 2, ENC_BIG_ENDIAN); proto_tree_add_item(opt_tree, hf_pim_optionlength, tvb, offset + 2, 2, ENC_BIG_ENDIAN); switch(hello_opt) { case 1: /* Hello Hold Time Option */ opt_value = tvb_get_ntohs(tvb, offset + 4); proto_tree_add_uint_format(opt_tree, hf_pim_holdtime, tvb, offset + 4, opt_len, opt_value, "Holdtime: %us %s", opt_value, opt_value == 0 ? "(goodbye)" : opt_value == 0xffff ? "(infinity)": ""); proto_item_append_text(opt_item, ": %us %s", opt_value, opt_value == 0 ? "(goodbye)" : opt_value == 0xffff ? "(infinity)": ""); break; case 2: /* LAN Prune Delay Option */ proto_tree_add_item(opt_tree, hf_pim_t, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item(opt_tree, hf_pim_propagation_delay, tvb, offset + 4, 2, ENC_BIG_ENDIAN); proto_tree_add_item(opt_tree, hf_pim_override_interval, tvb, offset + 6, 2, ENC_BIG_ENDIAN); proto_item_append_text(opt_item, ": T = %u, Propagation Delay = %ums, Override Interval = %ums", tvb_get_guint8(tvb, offset + 4) & 0x80 ? 1 : 0, tvb_get_ntohs(tvb, offset + 4) & 0x7fff, tvb_get_ntohs(tvb, offset + 6)); break; case 19: /* DR priority */ proto_tree_add_item(opt_tree, hf_pim_dr_priority, tvb, offset + 4, 4, ENC_BIG_ENDIAN); proto_item_append_text(opt_item, ": %u", tvb_get_ntohl(tvb, offset + 4)); break; case 20: /* Generation ID */ proto_tree_add_item(opt_tree, hf_pim_generation_id, tvb, offset + 4, 4, ENC_BIG_ENDIAN); proto_item_append_text(opt_item, ": %u", tvb_get_ntohl(tvb, offset + 4)); break; case 21: /* State Refresh Capable Option */ proto_tree_add_item(opt_tree, hf_pim_state_refresh_version, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item(opt_tree, hf_pim_state_refresh_interval, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item(opt_tree, hf_pim_state_refresh_reserved, tvb, offset + 6, 2, ENC_BIG_ENDIAN); proto_item_append_text(opt_item, ": Version = %u, Interval = %us", tvb_get_guint8(tvb, offset + 4), tvb_get_guint8(tvb, offset + 5)); break; case 24: /* address list */ case 65001: /* address list (old implementations) */ { int i; proto_tree *sub_tree = NULL; proto_item *addrlist_option; addrlist_option = proto_tree_add_text(opt_tree, tvb, offset, 4 + opt_len, "%sAddress List (%u)", hello_opt == 65001 ? "old " : "", hello_opt); sub_tree = proto_item_add_subtree(addrlist_option, ett_pim_opt); for (i = offset + 4; i < offset + 4 + opt_len; ) { int advance; const char *s; s = dissect_pim_addr(tvb, i, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(sub_tree, tvb, offset, advance, "Address: %s", s); i += advance; } break; } default: if (opt_len) proto_tree_add_item(opt_tree, hf_pim_optionvalue, tvb, offset + 4, opt_len, ENC_NA); break; } offset += 4 + opt_len; } proto_item_append_text(tiopt, ": %u", opt_count); break; } case 1: /* register */ { guint32 flags; guint8 v_hl; tvbuff_t *next_tvb; proto_tree *flag_tree = NULL; proto_item *tiflag; flags = tvb_get_ntohl(tvb, offset); tiflag = proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Flags: 0x%08x", flags); flag_tree = proto_item_add_subtree(tiflag, ett_pim); proto_tree_add_text(flag_tree, tvb, offset, 1, "%s", decode_boolean_bitfield(flags, 0x80000000, 32, "Border", "Not border")); proto_tree_add_text(flag_tree, tvb, offset, 1, "%s", decode_boolean_bitfield(flags, 0x40000000, 32, "Null-Register", "Not Null-Register")); offset += 4; /* * The rest of the packet is a multicast data packet. */ next_tvb = tvb_new_subset_remaining(tvb, offset); /* * It's an IP packet - determine whether it's IPv4 or IPv6. */ v_hl = tvb_get_guint8(tvb, offset); switch((v_hl & 0xf0) >> 4) { case 0: /* Null-Register dummy header. * Has the same address family as the encapsulating PIM packet, * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet. */ if (pinfo->src.type == AT_IPv4) { proto_tree_add_text(pimopt_tree, tvb, offset, -1, "IPv4 dummy header"); proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4, "Source: %s", tvb_ip_to_str(tvb, offset + 12)); proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4, "Group: %s", tvb_ip_to_str(tvb, offset + 16)); } else if (pinfo->src.type == AT_IPv6) { proto_tree_add_text(pimopt_tree, tvb, offset, -1, "IPv6 dummy header"); proto_tree_add_text(pimopt_tree, tvb, offset + 8, 16, "Source: %s", tvb_ip6_to_str(tvb, offset + 8)); proto_tree_add_text(pimopt_tree, tvb, offset + 8 + 16, 16, "Group: %s", tvb_ip6_to_str(tvb, offset + 8 + 16)); } else proto_tree_add_text(pimopt_tree, tvb, offset, -1, "Dummy header for an unknown protocol"); break; case 4: /* IPv4 */ #if 0 call_dissector(ip_handle, next_tvb, pinfo, tree); #else call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree); #endif break; case 6: /* IPv6 */ #if 0 call_dissector(ipv6_handle, next_tvb, pinfo, tree); #else call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree); #endif break; default: proto_tree_add_text(pimopt_tree, tvb, offset, -1, "Unknown IP version %d", (v_hl & 0xf0) >> 4); break; } break; } case 2: /* register-stop */ { int advance; const char *s; s = dissect_pim_addr(tvb, offset, pimv2_group, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s); offset += advance; s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s); break; } case 3: /* join/prune */ case 6: /* graft */ case 7: /* graft-ack */ { int advance; int off; const char *s; int ngroup, i, njoin, nprune, j; guint16 holdtime; proto_tree *grouptree = NULL; proto_item *tigroup; proto_tree *subtree = NULL; proto_item *tisub; s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Upstream-neighbor: %s", s); offset += advance; proto_tree_add_item(pimopt_tree, hf_pim_res_bytes, tvb, offset, 1, ENC_NA); offset += 1; /* skip reserved field */ ngroup = tvb_get_guint8(tvb, offset); proto_tree_add_item(pimopt_tree, hf_pim_numgroups, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; holdtime = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(pimopt_tree, hf_pim_holdtime, tvb, offset, 2, holdtime, "Holdtime: %us %s", holdtime, holdtime == 0xffff ? "(infinity)": ""); offset += 2; for (i = 0; i < ngroup; i++) { s = dissect_pim_addr(tvb, offset, pimv2_group, &advance); if (s == NULL) goto breakbreak3; tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group %d: %s", i, s); grouptree = proto_item_add_subtree(tigroup, ett_pim); offset += advance; njoin = tvb_get_ntohs(tvb, offset); nprune = tvb_get_ntohs(tvb, offset + 2); tisub = proto_tree_add_item(grouptree, hf_pim_numjoins, tvb, offset, 2, ENC_BIG_ENDIAN); subtree = proto_item_add_subtree(tisub, ett_pim); off = offset + 4; for (j = 0; j < njoin; j++) { s = dissect_pim_addr(tvb, off, pimv2_source, &advance); if (s == NULL) goto breakbreak3; proto_tree_add_text(subtree, tvb, off, advance, "IP address: %s", s); off += advance; } tisub = proto_tree_add_item(grouptree, hf_pim_numprunes, tvb, offset + 2, 2, ENC_BIG_ENDIAN); subtree = proto_item_add_subtree(tisub, ett_pim); for (j = 0; j < nprune; j++) { s = dissect_pim_addr(tvb, off, pimv2_source, &advance); if (s == NULL) goto breakbreak3; proto_tree_add_text(subtree, tvb, off, advance, "IP address: %s", s); off += advance; } offset = off; } breakbreak3: break; } case 4: /* bootstrap */ { const char *s; int advance; int i, j; int frpcnt; guint16 holdtime; proto_tree *grouptree = NULL; proto_item *tigroup; proto_tree_add_text(pimopt_tree, tvb, offset, 2, "Fragment tag: 0x%04x", tvb_get_ntohs(tvb, offset)); offset += 2; proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Hash mask len: %u", tvb_get_guint8(tvb, offset)); offset += 1; proto_tree_add_text(pimopt_tree, tvb, offset, 1, "BSR priority: %u", tvb_get_guint8(tvb, offset)); offset += 1; s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "BSR: %s", s); offset += advance; for (i = 0; tvb_reported_length_remaining(tvb, offset) > 0; i++) { s = dissect_pim_addr(tvb, offset, pimv2_group, &advance); if (s == NULL) goto breakbreak4; tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group %d: %s", i, s); grouptree = proto_item_add_subtree(tigroup, ett_pim); offset += advance; proto_tree_add_text(grouptree, tvb, offset, 1, "RP count: %u", tvb_get_guint8(tvb, offset)); offset += 1; frpcnt = tvb_get_guint8(tvb, offset); proto_tree_add_text(grouptree, tvb, offset, 1, "FRP count: %u", frpcnt); offset += 3; for (j = 0; j < frpcnt; j++) { s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) goto breakbreak4; proto_tree_add_text(grouptree, tvb, offset, advance, "RP %d: %s", j, s); offset += advance; holdtime = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(grouptree, hf_pim_holdtime, tvb, offset, 2, holdtime, "Holdtime: %us %s", holdtime, holdtime == 0xffff ? "(infinity)": ""); offset += 2; proto_tree_add_text(grouptree, tvb, offset, 1, "Priority: %u", tvb_get_guint8(tvb, offset)); offset += 2; /* also skips reserved field */ } } breakbreak4: break; } case 5: /* assert */ { const char *s; int advance; guint32 pref; s = dissect_pim_addr(tvb, offset, pimv2_group, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s); offset += advance; s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s); offset += advance; proto_tree_add_item(pimopt_tree, hf_pim_rpt, tvb, offset, 1, ENC_BIG_ENDIAN); pref = tvb_get_ntohl(tvb, offset) & 0x7fffffff; proto_tree_add_uint_format(pimopt_tree, hf_pim_metric_pref, tvb, offset, 4, pref, "Metric Preference: %u", pref); offset += 4; proto_tree_add_item(pimopt_tree, hf_pim_metric, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; break; } case 8: /* Candidate-RP-Advertisement */ { const char *s; int advance; int pfxcnt; guint16 holdtime; int i; pfxcnt = tvb_get_guint8(tvb, offset); proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Prefix-count: %u", pfxcnt); offset += 1; proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Priority: %u", tvb_get_guint8(tvb, offset)); offset += 1; holdtime = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(pimopt_tree, hf_pim_holdtime, tvb, offset, 2, holdtime, "Holdtime: %us %s", holdtime, holdtime == 0xffff ? "(infinity)": ""); offset += 2; s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "RP: %s", s); offset += advance; for (i = 0; i < pfxcnt; i++) { s = dissect_pim_addr(tvb, offset, pimv2_group, &advance); if (s == NULL) goto breakbreak8; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group %d: %s", i, s); offset += advance; } breakbreak8: break; } case 9: /* State-Refresh */ { const char *s; int advance; guint32 pref; s = dissect_pim_addr(tvb, offset, pimv2_group, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s); offset += advance; s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s); offset += advance; s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance); if (s == NULL) break; proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Originator: %s", s); offset += advance; proto_tree_add_item(pimopt_tree, hf_pim_rpt, tvb, offset, 1, ENC_BIG_ENDIAN); pref = tvb_get_ntohl(tvb, offset) & 0x7fffffff; proto_tree_add_uint_format(pimopt_tree, hf_pim_metric_pref, tvb, offset, 4, pref, "Metric Preference: %u", pref); offset += 4; proto_tree_add_item(pimopt_tree, hf_pim_metric, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Masklen: %u", tvb_get_guint8(tvb, offset)); offset += 1; proto_tree_add_text(pimopt_tree, tvb, offset, 1, "TTL: %u", tvb_get_guint8(tvb, offset)); offset += 1; proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Prune indicator %s", decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8, "set", "clear")); proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Prune now %s", decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x40, 8, "set", "clear")); proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Assert override %s", decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x20, 8, "set", "clear")); offset += 1; proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Interval: %u", tvb_get_guint8(tvb, offset)); offset += 1; break; } default: break; } done:; }
/** * dccp_print - show dccp packet * @bp - beginning of dccp packet * @data2 - beginning of enclosing * @len - lenght of ip packet */ void dccp_print(netdissect_options *ndo, const u_char *bp, const u_char *data2, u_int len) { const struct dccp_hdr *dh; const struct ip *ip; #ifdef INET6 const struct ip6_hdr *ip6; #endif const u_char *cp; u_short sport, dport; u_int hlen; u_int fixed_hdrlen; dh = (const struct dccp_hdr *)bp; ip = (struct ip *)data2; #ifdef INET6 if (IP_V(ip) == 6) ip6 = (const struct ip6_hdr *)data2; else ip6 = NULL; #endif /*INET6*/ /* make sure we have enough data to look at the X bit */ cp = (const u_char *)(dh + 1); if (cp > ndo->ndo_snapend) { ND_PRINT((ndo, "[Invalid packet|dccp]")); return; } if (len < sizeof(struct dccp_hdr)) { ND_PRINT((ndo, "truncated-dccp - %u bytes missing!", len - (u_int)sizeof(struct dccp_hdr))); return; } /* get the length of the generic header */ fixed_hdrlen = dccp_basic_hdr_len(dh); if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp - %u bytes missing!", len - fixed_hdrlen)); return; } ND_TCHECK2(*dh, fixed_hdrlen); sport = EXTRACT_16BITS(&dh->dccph_sport); dport = EXTRACT_16BITS(&dh->dccph_dport); hlen = dh->dccph_doff * 4; #ifdef INET6 if (ip6) { ND_PRINT((ndo, "%s.%d > %s.%d: ", ip6addr_string(ndo, &ip6->ip6_src), sport, ip6addr_string(ndo, &ip6->ip6_dst), dport)); } else #endif /*INET6*/ { ND_PRINT((ndo, "%s.%d > %s.%d: ", ipaddr_string(ndo, &ip->ip_src), sport, ipaddr_string(ndo, &ip->ip_dst), dport)); } if (ndo->ndo_qflag) { ND_PRINT((ndo, " %d", len - hlen)); if (hlen > len) { ND_PRINT((ndo, "dccp [bad hdr length %u - too long, > %u]", hlen, len)); } return; } /* other variables in generic header */ if (ndo->ndo_vflag) { ND_PRINT((ndo, "CCVal %d, CsCov %d, ", DCCPH_CCVAL(dh), DCCPH_CSCOV(dh))); } /* checksum calculation */ if (ndo->ndo_vflag && ND_TTEST2(bp[0], len)) { u_int16_t sum = 0, dccp_sum; dccp_sum = EXTRACT_16BITS(&dh->dccph_checksum); ND_PRINT((ndo, "cksum 0x%04x ", dccp_sum)); if (IP_V(ip) == 4) sum = dccp_cksum(ndo, ip, dh, len); #ifdef INET6 else if (IP_V(ip) == 6) sum = dccp6_cksum(ip6, dh, len); #endif if (sum != 0) ND_PRINT((ndo, "(incorrect -> 0x%04x), ",in_cksum_shouldbe(dccp_sum, sum))); else ND_PRINT((ndo, "(correct), ")); } switch (DCCPH_TYPE(dh)) { case DCCP_PKT_REQUEST: { struct dccp_hdr_request *dhr = (struct dccp_hdr_request *)(bp + fixed_hdrlen); fixed_hdrlen += 4; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp request - %u bytes missing!", len - fixed_hdrlen)); return; } ND_TCHECK(*dhr); ND_PRINT((ndo, "request (service=%d) ", EXTRACT_32BITS(&dhr->dccph_req_service))); break; } case DCCP_PKT_RESPONSE: { struct dccp_hdr_response *dhr = (struct dccp_hdr_response *)(bp + fixed_hdrlen); fixed_hdrlen += 12; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp response - %u bytes missing!", len - fixed_hdrlen)); return; } ND_TCHECK(*dhr); ND_PRINT((ndo, "response (service=%d) ", EXTRACT_32BITS(&dhr->dccph_resp_service))); break; } case DCCP_PKT_DATA: ND_PRINT((ndo, "data ")); break; case DCCP_PKT_ACK: { fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp ack - %u bytes missing!", len - fixed_hdrlen)); return; } ND_PRINT((ndo, "ack ")); break; } case DCCP_PKT_DATAACK: { fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp dataack - %u bytes missing!", len - fixed_hdrlen)); return; } ND_PRINT((ndo, "dataack ")); break; } case DCCP_PKT_CLOSEREQ: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp closereq - %u bytes missing!", len - fixed_hdrlen)); return; } ND_PRINT((ndo, "closereq ")); break; case DCCP_PKT_CLOSE: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp close - %u bytes missing!", len - fixed_hdrlen)); return; } ND_PRINT((ndo, "close ")); break; case DCCP_PKT_RESET: { struct dccp_hdr_reset *dhr = (struct dccp_hdr_reset *)(bp + fixed_hdrlen); fixed_hdrlen += 12; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp reset - %u bytes missing!", len - fixed_hdrlen)); return; } ND_TCHECK(*dhr); ND_PRINT((ndo, "reset (code=%s) ", dccp_reset_code(dhr->dccph_reset_code))); break; } case DCCP_PKT_SYNC: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp sync - %u bytes missing!", len - fixed_hdrlen)); return; } ND_PRINT((ndo, "sync ")); break; case DCCP_PKT_SYNCACK: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT((ndo, "truncated-dccp syncack - %u bytes missing!", len - fixed_hdrlen)); return; } ND_PRINT((ndo, "syncack ")); break; default: ND_PRINT((ndo, "invalid ")); break; } if ((DCCPH_TYPE(dh) != DCCP_PKT_DATA) && (DCCPH_TYPE(dh) != DCCP_PKT_REQUEST)) dccp_print_ack_no(ndo, bp); if (ndo->ndo_vflag < 2) return; ND_PRINT((ndo, "seq %" PRIu64, dccp_seqno(bp))); /* process options */ if (hlen > fixed_hdrlen){ const u_char *cp; u_int optlen; cp = bp + fixed_hdrlen; ND_PRINT((ndo, " <")); hlen -= fixed_hdrlen; while(1){ optlen = dccp_print_option(ndo, cp, hlen); if (!optlen) break; if (hlen <= optlen) break; hlen -= optlen; cp += optlen; ND_PRINT((ndo, ", ")); } ND_PRINT((ndo, ">")); } return; trunc: ND_PRINT((ndo, "%s", tstr)); return; }
void icmp_print(const u_char *bp, u_int plen, const u_char *bp2, int fragmented) { char *cp; const struct icmp *dp; const struct icmp_ext_t *ext_dp; const struct ip *ip; const char *str, *fmt; const struct ip *oip; const struct udphdr *ouh; const u_int8_t *obj_tptr; u_int32_t raw_label; const u_char *snapend_save; const struct icmp_mpls_ext_object_header_t *icmp_mpls_ext_object_header; u_int hlen, dport, mtu, obj_tlen, obj_class_num, obj_ctype; char buf[MAXHOSTNAMELEN + 100]; dp = (struct icmp *)bp; ext_dp = (struct icmp_ext_t *)bp; ip = (struct ip *)bp2; str = buf; TCHECK(dp->icmp_code); switch (dp->icmp_type) { case ICMP_ECHO: case ICMP_ECHOREPLY: TCHECK(dp->icmp_seq); (void)snprintf(buf, sizeof(buf), "echo %s, id %u, seq %u", dp->icmp_type == ICMP_ECHO ? "request" : "reply", EXTRACT_16BITS(&dp->icmp_id), EXTRACT_16BITS(&dp->icmp_seq)); break; case ICMP_UNREACH: TCHECK(dp->icmp_ip.ip_dst); switch (dp->icmp_code) { case ICMP_UNREACH_PROTOCOL: TCHECK(dp->icmp_ip.ip_p); (void)snprintf(buf, sizeof(buf), "%s protocol %d unreachable", ipaddr_string(&dp->icmp_ip.ip_dst), dp->icmp_ip.ip_p); break; case ICMP_UNREACH_PORT: TCHECK(dp->icmp_ip.ip_p); oip = &dp->icmp_ip; hlen = IP_HL(oip) * 4; ouh = (struct udphdr *)(((u_char *)oip) + hlen); TCHECK(ouh->uh_dport); dport = EXTRACT_16BITS(&ouh->uh_dport); switch (oip->ip_p) { case IPPROTO_TCP: (void)snprintf(buf, sizeof(buf), "%s tcp port %s unreachable", ipaddr_string(&oip->ip_dst), tcpport_string(dport)); break; case IPPROTO_UDP: (void)snprintf(buf, sizeof(buf), "%s udp port %s unreachable", ipaddr_string(&oip->ip_dst), udpport_string(dport)); break; default: (void)snprintf(buf, sizeof(buf), "%s protocol %d port %d unreachable", ipaddr_string(&oip->ip_dst), oip->ip_p, dport); break; } break; case ICMP_UNREACH_NEEDFRAG: { register const struct mtu_discovery *mp; mp = (struct mtu_discovery *)(u_char *)&dp->icmp_void; mtu = EXTRACT_16BITS(&mp->nexthopmtu); if (mtu) { (void)snprintf(buf, sizeof(buf), "%s unreachable - need to frag (mtu %d)", ipaddr_string(&dp->icmp_ip.ip_dst), mtu); } else { (void)snprintf(buf, sizeof(buf), "%s unreachable - need to frag", ipaddr_string(&dp->icmp_ip.ip_dst)); } } break; default: fmt = tok2str(unreach2str, "#%d %%s unreachable", dp->icmp_code); (void)snprintf(buf, sizeof(buf), fmt, ipaddr_string(&dp->icmp_ip.ip_dst)); break; } break; case ICMP_REDIRECT: TCHECK(dp->icmp_ip.ip_dst); fmt = tok2str(type2str, "redirect-#%d %%s to net %%s", dp->icmp_code); (void)snprintf(buf, sizeof(buf), fmt, ipaddr_string(&dp->icmp_ip.ip_dst), ipaddr_string(&dp->icmp_gwaddr)); break; case ICMP_ROUTERADVERT: { register const struct ih_rdiscovery *ihp; register const struct id_rdiscovery *idp; u_int lifetime, num, size; (void)snprintf(buf, sizeof(buf), "router advertisement"); cp = buf + strlen(buf); ihp = (struct ih_rdiscovery *)&dp->icmp_void; TCHECK(*ihp); (void)strncpy(cp, " lifetime ", sizeof(buf) - (cp - buf)); cp = buf + strlen(buf); lifetime = EXTRACT_16BITS(&ihp->ird_lifetime); if (lifetime < 60) { (void)snprintf(cp, sizeof(buf) - (cp - buf), "%u", lifetime); } else if (lifetime < 60 * 60) { (void)snprintf(cp, sizeof(buf) - (cp - buf), "%u:%02u", lifetime / 60, lifetime % 60); } else { (void)snprintf(cp, sizeof(buf) - (cp - buf), "%u:%02u:%02u", lifetime / 3600, (lifetime % 3600) / 60, lifetime % 60); } cp = buf + strlen(buf); num = ihp->ird_addrnum; (void)snprintf(cp, sizeof(buf) - (cp - buf), " %d:", num); cp = buf + strlen(buf); size = ihp->ird_addrsiz; if (size != 2) { (void)snprintf(cp, sizeof(buf) - (cp - buf), " [size %d]", size); break; } idp = (struct id_rdiscovery *)&dp->icmp_data; while (num-- > 0) { TCHECK(*idp); (void)snprintf(cp, sizeof(buf) - (cp - buf), " {%s %u}", ipaddr_string(&idp->ird_addr), EXTRACT_32BITS(&idp->ird_pref)); cp = buf + strlen(buf); ++idp; } } break; case ICMP_TIMXCEED: TCHECK(dp->icmp_ip.ip_dst); switch (dp->icmp_code) { case ICMP_TIMXCEED_INTRANS: str = "time exceeded in-transit"; break; case ICMP_TIMXCEED_REASS: str = "ip reassembly time exceeded"; break; default: (void)snprintf(buf, sizeof(buf), "time exceeded-#%d", dp->icmp_code); break; } break; case ICMP_PARAMPROB: if (dp->icmp_code) (void)snprintf(buf, sizeof(buf), "parameter problem - code %d", dp->icmp_code); else { TCHECK(dp->icmp_pptr); (void)snprintf(buf, sizeof(buf), "parameter problem - octet %d", dp->icmp_pptr); } break; case ICMP_MASKREPLY: TCHECK(dp->icmp_mask); (void)snprintf(buf, sizeof(buf), "address mask is 0x%08x", EXTRACT_32BITS(&dp->icmp_mask)); break; case ICMP_TSTAMP: TCHECK(dp->icmp_seq); (void)snprintf(buf, sizeof(buf), "time stamp query id %u seq %u", EXTRACT_16BITS(&dp->icmp_id), EXTRACT_16BITS(&dp->icmp_seq)); break; case ICMP_TSTAMPREPLY: TCHECK(dp->icmp_ttime); (void)snprintf(buf, sizeof(buf), "time stamp reply id %u seq %u: org %s", EXTRACT_16BITS(&dp->icmp_id), EXTRACT_16BITS(&dp->icmp_seq), icmp_tstamp_print(EXTRACT_32BITS(&dp->icmp_otime))); (void)snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),", recv %s", icmp_tstamp_print(EXTRACT_32BITS(&dp->icmp_rtime))); (void)snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),", xmit %s", icmp_tstamp_print(EXTRACT_32BITS(&dp->icmp_ttime))); break; default: str = tok2str(icmp2str, "type-#%d", dp->icmp_type); break; } (void)printf("ICMP %s, length %u", str, plen); if (vflag && !fragmented) { /* don't attempt checksumming if this is a frag */ u_int16_t sum, icmp_sum; if (TTEST2(*bp, plen)) { sum = in_cksum((u_short*)dp, plen, 0); if (sum != 0) { icmp_sum = EXTRACT_16BITS(&dp->icmp_cksum); (void)printf(" (wrong icmp cksum %x (->%x)!)", icmp_sum, in_cksum_shouldbe(icmp_sum, sum)); } } } /* * print the remnants of the IP packet. * save the snaplength as this may get overidden in the IP printer. */ if (vflag >= 1 && !ICMP_INFOTYPE(dp->icmp_type)) { bp += 8; (void)printf("\n\t"); ip = (struct ip *)bp; snaplen = snapend - bp; snapend_save = snapend; ip_print(gndo, bp, EXTRACT_16BITS(&ip->ip_len)); snapend = snapend_save; } /* * Attempt to decode the MPLS extensions only for some ICMP types. */ if (vflag >= 1 && plen > ICMP_EXTD_MINLEN && ICMP_MPLS_EXT_TYPE(dp->icmp_type)) { TCHECK(*ext_dp); /* * Check first if the mpls extension header shows a non-zero length. * If the length field is not set then silently verify the checksum * to check if an extension header is present. This is expedient, * however not all implementations set the length field proper. */ if (!ext_dp->icmp_length && in_cksum((const u_short *)&ext_dp->icmp_ext_version_res, plen - ICMP_EXTD_MINLEN, 0)) { return; } printf("\n\tMPLS extension v%u", ICMP_MPLS_EXT_EXTRACT_VERSION(*(ext_dp->icmp_ext_version_res))); /* * Sanity checking of the header. */ if (ICMP_MPLS_EXT_EXTRACT_VERSION(*(ext_dp->icmp_ext_version_res)) != ICMP_MPLS_EXT_VERSION) { printf(" packet not supported"); return; } hlen = plen - ICMP_EXTD_MINLEN; printf(", checksum 0x%04x (%scorrect), length %u", EXTRACT_16BITS(ext_dp->icmp_ext_checksum), in_cksum((const u_short *)&ext_dp->icmp_ext_version_res, plen - ICMP_EXTD_MINLEN, 0) ? "in" : "", hlen); hlen -= 4; /* subtract common header size */ obj_tptr = (u_int8_t *)ext_dp->icmp_ext_data; while (hlen > sizeof(struct icmp_mpls_ext_object_header_t)) { icmp_mpls_ext_object_header = (struct icmp_mpls_ext_object_header_t *)obj_tptr; TCHECK(*icmp_mpls_ext_object_header); obj_tlen = EXTRACT_16BITS(icmp_mpls_ext_object_header->length); obj_class_num = icmp_mpls_ext_object_header->class_num; obj_ctype = icmp_mpls_ext_object_header->ctype; obj_tptr += sizeof(struct icmp_mpls_ext_object_header_t); printf("\n\t %s Object (%u), Class-Type: %u, length %u", tok2str(icmp_mpls_ext_obj_values,"unknown",obj_class_num), obj_class_num, obj_ctype, obj_tlen); hlen-=sizeof(struct icmp_mpls_ext_object_header_t); /* length field includes tlv header */ /* infinite loop protection */ if ((obj_class_num == 0) || (obj_tlen < sizeof(struct icmp_mpls_ext_object_header_t))) { return; } obj_tlen-=sizeof(struct icmp_mpls_ext_object_header_t); switch (obj_class_num) { case 1: switch(obj_ctype) { case 1: TCHECK2(*obj_tptr, 4); raw_label = EXTRACT_32BITS(obj_tptr); printf("\n\t label %u, exp %u", MPLS_LABEL(raw_label), MPLS_EXP(raw_label)); if (MPLS_STACK(raw_label)) printf(", [S]"); printf(", ttl %u", MPLS_TTL(raw_label)); break; default: print_unknown_data(obj_tptr, "\n\t ", obj_tlen); } break; /* * FIXME those are the defined objects that lack a decoder * you are welcome to contribute code ;-) */ case 2: default: print_unknown_data(obj_tptr, "\n\t ", obj_tlen); break; } if (hlen < obj_tlen) break; hlen -= obj_tlen; obj_tptr += obj_tlen; } } return; trunc: fputs("[|icmp]", stdout); }
/* This function is only called from the IGMP dissector */ int dissect_pimv1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) { guint8 pim_type; guint8 pim_ver; guint length, pim_length; guint16 pim_cksum, computed_cksum; vec_t cksum_vec[1]; proto_tree *pim_tree = NULL; proto_item *ti; proto_tree *pimopt_tree = NULL; proto_item *tiopt; if (!proto_is_protocol_enabled(find_protocol_by_id(proto_pim))) { /* * We are not enabled; skip entire packet to be nice to the * IGMP layer (so clicking on IGMP will display the data). */ return offset+tvb_length_remaining(tvb, offset); } col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIMv1"); col_clear(pinfo->cinfo, COL_INFO); ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, ENC_NA); pim_tree = proto_item_add_subtree(ti, ett_pim); /* Put IGMP type, 0x14, into the tree */ proto_tree_add_text(pim_tree, tvb, offset, 1, "Type: PIM (0x14)"); offset += 1; pim_type = tvb_get_guint8(tvb, offset); if (check_col(pinfo->cinfo, COL_INFO)) col_add_str(pinfo->cinfo, COL_INFO, val_to_str(pim_type, type1vals, "Unknown (%u)")); proto_tree_add_uint(pim_tree, hf_pim_code, tvb, offset, 1, pim_type); offset += 1; pim_cksum = tvb_get_ntohs(tvb, offset); pim_ver = PIM_VER(tvb_get_guint8(tvb, offset + 2)); if (pim_ver != 1) { /* * Not PIMv1 - what gives? */ proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb, offset, 2, pim_cksum); offset += 2; proto_tree_add_item(pim_tree, hf_pim_version, tvb, offset, 1, ENC_BIG_ENDIAN); return offset+tvb_length_remaining(tvb, offset); } /* * Well, it's PIM v1, so we can check whether this is a * Register message, and thus can figure out how much to * checksum and whether to make the columns read-only. */ length = tvb_length(tvb); if (pim_type == 1) { /* * Register message - the PIM header is 8 bytes long. * Also set the columns non-writable. Otherwise the IPv4 or * IPv6 dissector for the encapsulated packet that caused * this register will overwrite the PIM info in the columns. */ pim_length = 8; col_set_writable(pinfo->cinfo, FALSE); } else { /* * Other message - checksum the entire packet. */ pim_length = tvb_reported_length(tvb); } if (!pinfo->fragmented && length >= pim_length) { /* * The packet isn't part of a fragmented datagram and isn't * truncated, so we can checksum it. */ cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length); cksum_vec[0].len = pim_length; computed_cksum = in_cksum(&cksum_vec[0], 1); if (computed_cksum == 0) { proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb, offset, 2, pim_cksum, "Checksum: 0x%04x [correct]", pim_cksum); } else { proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb, offset, 2, pim_cksum, "Checksum: 0x%04x [incorrect, should be 0x%04x]", pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum)); } } else { proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb, offset, 2, pim_cksum); } offset += 2; proto_tree_add_item(pim_tree, hf_pim_version, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; offset += 3; /* skip reserved stuff */ if (tvb_reported_length_remaining(tvb, offset) > 0) { tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1, "PIM options"); pimopt_tree = proto_item_add_subtree(tiopt, ett_pim_opts); } else goto done; /* version 1 decoder */ switch (pim_type) { case 0: /* query */ { guint16 holdtime; proto_tree_add_item(pimopt_tree, hf_pim_mode, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 2; holdtime = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(pimopt_tree, hf_pim_holdtime, tvb, offset, 2, holdtime, "Holdtime: %us %s", holdtime, holdtime == 0xffff ? "(infinity)": ""); offset += 2; break; } case 1: /* register */ { guint8 v_hl; tvbuff_t *next_tvb; /* * The rest of the packet is a multicast data packet. */ next_tvb = tvb_new_subset_remaining(tvb, offset); /* * It's an IP packet - determine whether it's IPv4 or IPv6. */ v_hl = tvb_get_guint8(tvb, offset); switch((v_hl & 0xf0) >> 4) { case 0: /* Null-Register dummy header. * Has the same address family as the encapsulating PIM packet, * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet. */ if (pinfo->src.type == AT_IPv4) { proto_tree_add_text(pimopt_tree, tvb, offset, -1, "IPv4 dummy header"); proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4, "Source: %s", tvb_ip_to_str(tvb, offset + 12)); proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4, "Group: %s", tvb_ip_to_str(tvb, offset + 16)); } else if (pinfo->src.type == AT_IPv6) { proto_tree_add_text(pimopt_tree, tvb, offset, -1, "IPv6 dummy header"); proto_tree_add_text(pimopt_tree, tvb, offset + 8, 16, "Source: %s", tvb_ip6_to_str(tvb, offset + 8)); proto_tree_add_text(pimopt_tree, tvb, offset + 8 + 16, 16, "Group: %s", tvb_ip6_to_str(tvb, offset + 8)); } else proto_tree_add_text(pimopt_tree, tvb, offset, -1, "Dummy header for an unknown protocol"); break; case 4: /* IPv4 */ #if 0 call_dissector(ip_handle, next_tvb, pinfo, tree); #else call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree); #endif break; case 6: /* IPv6 */ #if 0 call_dissector(ipv6_handle, next_tvb, pinfo, tree); #else call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree); #endif break; default: proto_tree_add_text(pimopt_tree, tvb, offset, -1, "Unknown IP version %d", (v_hl & 0xf0) >> 4); break; } break; } case 2: /* register-stop */ { proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Group: %s", tvb_ip_to_str(tvb, offset)); offset += 4; proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Source: %s", tvb_ip_to_str(tvb, offset)); offset += 4; break; } case 3: /* join/prune */ case 6: /* graft */ case 7: /* graft-ack */ { int off; const char *s; int ngroup, i, njoin, nprune, j; guint16 holdtime; guint8 mask_len; guint8 adr_len; proto_tree *grouptree = NULL; proto_item *tigroup; proto_tree *subtree = NULL; proto_item *tisub; proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Upstream-neighbor: %s", tvb_ip_to_str(tvb, offset)); offset += 4; offset += 2; /* skip reserved stuff */ holdtime = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(pimopt_tree, hf_pim_holdtime, tvb, offset, 2, holdtime, "Holdtime: %us %s", holdtime, holdtime == 0xffff ? "(infinity)": ""); offset += 2; offset += 1; /* skip reserved stuff */ mask_len = tvb_get_guint8(tvb, offset); proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Mask length: %u", mask_len); offset += 1; adr_len = tvb_get_guint8(tvb, offset); proto_tree_add_text(pimopt_tree, tvb, offset, 1, "Address length: %u", adr_len); offset += 1; ngroup = tvb_get_guint8(tvb, offset); proto_tree_add_item(pimopt_tree, hf_pim_numgroups, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; for (i = 0; i < ngroup; i++) { /* * XXX - does the group address have the length "adr_len" * and the group mask the length "mask_len"? */ tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Group %d: %s", i, tvb_ip_to_str(tvb, offset)); grouptree = proto_item_add_subtree(tigroup, ett_pim); offset += 4; proto_tree_add_text(grouptree, tvb, offset, 4, "Group %d Mask: %s", i, tvb_ip_to_str(tvb, offset)); offset += 4; njoin = tvb_get_ntohs(tvb, offset); nprune = tvb_get_ntohs(tvb, offset + 2); tisub = proto_tree_add_item(grouptree, hf_pim_numjoins, tvb, offset, 2, ENC_BIG_ENDIAN); subtree = proto_item_add_subtree(tisub, ett_pim); off = offset + 4; for (j = 0; j < njoin; j++) { s = dissect_pimv1_addr(tvb, off); proto_tree_add_text(subtree, tvb, off, 6, "IP address: %s", s); off += 6; } tisub = proto_tree_add_item(grouptree, hf_pim_numprunes, tvb, offset + 2, 2, ENC_BIG_ENDIAN); subtree = proto_item_add_subtree(tisub, ett_pim); for (j = 0; j < nprune; j++) { s = dissect_pimv1_addr(tvb, off); proto_tree_add_text(subtree, tvb, off, 6, "IP address: %s", s); off += 6; } offset = off; } break; } case 4: /* rp-reachability */ { guint16 holdtime; proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Group Address: %s", tvb_ip_to_str(tvb, offset)); offset += 4; proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Group Mask: %s", tvb_ip_to_str(tvb, offset)); offset += 4; proto_tree_add_text(pimopt_tree, tvb, offset, 4, "RP Address: %s", tvb_ip_to_str(tvb, offset)); offset += 4; offset += 2; /* skip reserved stuff */ holdtime = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(pimopt_tree, hf_pim_holdtime, tvb, offset, 2, holdtime, "Holdtime: %us %s", holdtime, holdtime == 0xffff ? "(infinity)": ""); offset += 2; break; } case 5: /* assert */ { guint32 pref; proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Group Address: %s", tvb_ip_to_str(tvb, offset)); offset += 4; proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Group Mask: %s", tvb_ip_to_str(tvb, offset)); offset += 4; proto_tree_add_item(pimopt_tree, hf_pim_rpt, tvb, offset, 1, ENC_BIG_ENDIAN); pref = tvb_get_ntohl(tvb, offset) & 0x7fffffff; proto_tree_add_uint_format(pimopt_tree, hf_pim_metric_pref, tvb, offset, 4, pref, "Metric Preference: %u", pref); offset += 4; proto_tree_add_item(pimopt_tree, hf_pim_metric, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; break; } default: break; } done:; return offset+tvb_length_remaining(tvb, offset); }
static void dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 ip_proto) { proto_tree *udp_tree = NULL; proto_item *ti, *hidden_item, *port_item; guint len; guint reported_len; vec_t cksum_vec[4]; guint32 phdr[2]; guint16 computed_cksum; int offset = 0; e_udphdr *udph; proto_tree *checksum_tree; proto_item *item; conversation_t *conv = NULL; struct udp_analysis *udpd = NULL; proto_tree *process_tree; udph=ep_new(e_udphdr); SET_ADDRESS(&udph->ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data); SET_ADDRESS(&udph->ip_dst, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data); col_set_str(pinfo->cinfo, COL_PROTOCOL, (ip_proto == IP_PROTO_UDP) ? "UDP" : "UDPlite"); col_clear(pinfo->cinfo, COL_INFO); udph->uh_sport=tvb_get_ntohs(tvb, offset); udph->uh_dport=tvb_get_ntohs(tvb, offset+2); col_add_fstr(pinfo->cinfo, COL_INFO, "Source port: %s Destination port: %s", get_udp_port(udph->uh_sport), get_udp_port(udph->uh_dport)); if (tree) { if (udp_summary_in_tree) { if (ip_proto == IP_PROTO_UDP) { ti = proto_tree_add_protocol_format(tree, proto_udp, tvb, offset, 8, "User Datagram Protocol, Src Port: %s (%u), Dst Port: %s (%u)", get_udp_port(udph->uh_sport), udph->uh_sport, get_udp_port(udph->uh_dport), udph->uh_dport); } else { ti = proto_tree_add_protocol_format(tree, proto_udplite, tvb, offset, 8, "Lightweight User Datagram Protocol, Src Port: %s (%u), Dst Port: %s (%u)", get_udp_port(udph->uh_sport), udph->uh_sport, get_udp_port(udph->uh_dport), udph->uh_dport); } } else { ti = proto_tree_add_item(tree, (ip_proto == IP_PROTO_UDP) ? proto_udp : proto_udplite, tvb, offset, 8, ENC_NA); } udp_tree = proto_item_add_subtree(ti, ett_udp); port_item = proto_tree_add_uint_format(udp_tree, hf_udp_srcport, tvb, offset, 2, udph->uh_sport, "Source port: %s (%u)", get_udp_port(udph->uh_sport), udph->uh_sport); /* The beginning port number, 32768 + 666 (33434), is from LBL's traceroute.c source code and this code * further assumes that 3 attempts are made per hop */ if(udph->uh_sport > 32768 + 666 && udph->uh_sport <= 32768 + 666 + 30) expert_add_info_format(pinfo, port_item, PI_SEQUENCE, PI_CHAT, "Possible traceroute: hop #%u, attempt #%u", ((udph->uh_sport - 32768 - 666 - 1) / 3) + 1, ((udph->uh_sport - 32768 - 666 - 1) % 3) + 1 ); port_item = proto_tree_add_uint_format(udp_tree, hf_udp_dstport, tvb, offset + 2, 2, udph->uh_dport, "Destination port: %s (%u)", get_udp_port(udph->uh_dport), udph->uh_dport); if(udph->uh_dport > 32768 + 666 && udph->uh_dport <= 32768 + 666 + 30) expert_add_info_format(pinfo, port_item, PI_SEQUENCE, PI_CHAT, "Possible traceroute: hop #%u, attempt #%u", ((udph->uh_dport - 32768 - 666 - 1) / 3) + 1, ((udph->uh_dport - 32768 - 666 - 1) % 3) + 1 ); hidden_item = proto_tree_add_uint(udp_tree, hf_udp_port, tvb, offset, 2, udph->uh_sport); PROTO_ITEM_SET_HIDDEN(hidden_item); hidden_item = proto_tree_add_uint(udp_tree, hf_udp_port, tvb, offset+2, 2, udph->uh_dport); PROTO_ITEM_SET_HIDDEN(hidden_item); } if (ip_proto == IP_PROTO_UDP) { udph->uh_ulen = udph->uh_sum_cov = tvb_get_ntohs(tvb, offset+4); if (udph->uh_ulen < 8) { /* Bogus length - it includes the header, so it must be >= 8. */ /* XXX - should handle IPv6 UDP jumbograms (RFC 2675), where the length is zero */ item = proto_tree_add_uint_format(udp_tree, hf_udp_length, tvb, offset + 4, 2, udph->uh_ulen, "Length: %u (bogus, must be >= 8)", udph->uh_ulen); expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR, "Bad length value %u < 8", udph->uh_ulen); col_append_fstr(pinfo->cinfo, COL_INFO, " [BAD UDP LENGTH %u < 8]", udph->uh_ulen); return; } if ((udph->uh_ulen > tvb_reported_length(tvb)) && ! pinfo->fragmented && ! pinfo->flags.in_error_pkt) { /* Bogus length - it goes past the end of the IP payload */ item = proto_tree_add_uint_format(udp_tree, hf_udp_length, tvb, offset + 4, 2, udph->uh_ulen, "Length: %u (bogus, payload length %u)", udph->uh_ulen, tvb_reported_length(tvb)); expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR, "Bad length value %u > IP payload length", udph->uh_ulen); col_append_fstr(pinfo->cinfo, COL_INFO, " [BAD UDP LENGTH %u > IP PAYLOAD LENGTH]", udph->uh_ulen); } else { if (tree) { proto_tree_add_uint(udp_tree, hf_udp_length, tvb, offset + 4, 2, udph->uh_ulen); /* XXX - why is this here, given that this is UDP, not Lightweight UDP? */ hidden_item = proto_tree_add_uint(udp_tree, hf_udplite_checksum_coverage, tvb, offset + 4, 0, udph->uh_sum_cov); PROTO_ITEM_SET_HIDDEN(hidden_item); } } } else { udph->uh_ulen = pinfo->iplen - pinfo->iphdrlen; udph->uh_sum_cov = tvb_get_ntohs(tvb, offset+4); if (((udph->uh_sum_cov > 0) && (udph->uh_sum_cov < 8)) || (udph->uh_sum_cov > udph->uh_ulen)) { /* Bogus length - it includes the header, so it must be >= 8, and no larger then the IP payload size. */ if (tree) { hidden_item = proto_tree_add_boolean(udp_tree, hf_udplite_checksum_coverage_bad, tvb, offset + 4, 2, TRUE); PROTO_ITEM_SET_HIDDEN(hidden_item); hidden_item = proto_tree_add_uint(udp_tree, hf_udp_length, tvb, offset + 4, 0, udph->uh_ulen); PROTO_ITEM_SET_HIDDEN(hidden_item); } item = proto_tree_add_uint_format(udp_tree, hf_udplite_checksum_coverage, tvb, offset + 4, 2, udph->uh_sum_cov, "Checksum coverage: %u (bogus, must be >= 8 and <= %u (ip.len-ip.hdr_len))", udph->uh_sum_cov, udph->uh_ulen); expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR, "Bad checksum coverage length value %u < 8 or > %u", udph->uh_sum_cov, udph->uh_ulen); col_append_fstr(pinfo->cinfo, COL_INFO, " [BAD LIGHTWEIGHT UDP CHECKSUM COVERAGE LENGTH %u < 8 or > %u]", udph->uh_sum_cov, udph->uh_ulen); if (!udplite_ignore_checksum_coverage) return; } else { if (tree) { hidden_item = proto_tree_add_uint(udp_tree, hf_udp_length, tvb, offset + 4, 0, udph->uh_ulen); PROTO_ITEM_SET_HIDDEN(hidden_item); proto_tree_add_uint(udp_tree, hf_udplite_checksum_coverage, tvb, offset + 4, 2, udph->uh_sum_cov); } } } udph->uh_sum_cov = (udph->uh_sum_cov) ? udph->uh_sum_cov : udph->uh_ulen; udph->uh_sum = tvb_get_ntohs(tvb, offset+6); reported_len = tvb_reported_length(tvb); len = tvb_length(tvb); if (udph->uh_sum == 0) { /* No checksum supplied in the packet. */ if ((ip_proto == IP_PROTO_UDP) && (pinfo->src.type == AT_IPv4)) { item = proto_tree_add_uint_format(udp_tree, hf_udp_checksum, tvb, offset + 6, 2, 0, "Checksum: 0x%04x (none)", 0); checksum_tree = proto_item_add_subtree(item, ett_udp_checksum); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_good, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_bad, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); } else { item = proto_tree_add_uint_format(udp_tree, hf_udp_checksum, tvb, offset + 6, 2, 0, "Checksum: 0x%04x (Illegal)", 0); expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR, "Illegal Checksum value (0)"); col_append_fstr(pinfo->cinfo, COL_INFO, " [ILLEGAL CHECKSUM (0)]"); checksum_tree = proto_item_add_subtree(item, ett_udp_checksum); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_good, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_bad, tvb, offset + 6, 2, TRUE); PROTO_ITEM_SET_GENERATED(item); } } else if (!pinfo->fragmented && len >= reported_len && len >= udph->uh_sum_cov && reported_len >= udph->uh_sum_cov && udph->uh_sum_cov >=8) { /* The packet isn't part of a fragmented datagram and isn't truncated, so we can checksum it. XXX - make a bigger scatter-gather list once we do fragment reassembly? */ if (((ip_proto == IP_PROTO_UDP) && (udp_check_checksum)) || ((ip_proto == IP_PROTO_UDPLITE) && (udplite_check_checksum))) { /* Set up the fields of the pseudo-header. */ cksum_vec[0].ptr = (const guint8 *)pinfo->src.data; cksum_vec[0].len = pinfo->src.len; cksum_vec[1].ptr = (const guint8 *)pinfo->dst.data; cksum_vec[1].len = pinfo->dst.len; cksum_vec[2].ptr = (const guint8 *)&phdr; switch (pinfo->src.type) { case AT_IPv4: if (ip_proto == IP_PROTO_UDP) phdr[0] = g_htonl((ip_proto<<16) | udph->uh_ulen); else phdr[0] = g_htonl((ip_proto<<16) | reported_len); cksum_vec[2].len = 4; break; case AT_IPv6: if (ip_proto == IP_PROTO_UDP) phdr[0] = g_htonl(udph->uh_ulen); else phdr[0] = g_htonl(reported_len); phdr[1] = g_htonl(ip_proto); cksum_vec[2].len = 8; break; default: /* UDP runs only atop IPv4 and IPv6.... */ DISSECTOR_ASSERT_NOT_REACHED(); break; } cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, udph->uh_sum_cov); cksum_vec[3].len = udph->uh_sum_cov; computed_cksum = in_cksum(&cksum_vec[0], 4); if (computed_cksum == 0) { item = proto_tree_add_uint_format(udp_tree, hf_udp_checksum, tvb, offset + 6, 2, udph->uh_sum, "Checksum: 0x%04x [correct]", udph->uh_sum); checksum_tree = proto_item_add_subtree(item, ett_udp_checksum); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_good, tvb, offset + 6, 2, TRUE); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_bad, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); } else { item = proto_tree_add_uint_format(udp_tree, hf_udp_checksum, tvb, offset + 6, 2, udph->uh_sum, "Checksum: 0x%04x [incorrect, should be 0x%04x (maybe caused by \"UDP checksum offload\"?)]", udph->uh_sum, in_cksum_shouldbe(udph->uh_sum, computed_cksum)); checksum_tree = proto_item_add_subtree(item, ett_udp_checksum); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_good, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_bad, tvb, offset + 6, 2, TRUE); PROTO_ITEM_SET_GENERATED(item); expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR, "Bad checksum"); col_append_fstr(pinfo->cinfo, COL_INFO, " [UDP CHECKSUM INCORRECT]"); } } else { item = proto_tree_add_uint_format(udp_tree, hf_udp_checksum, tvb, offset + 6, 2, udph->uh_sum, "Checksum: 0x%04x [validation disabled]", udph->uh_sum); checksum_tree = proto_item_add_subtree(item, ett_udp_checksum); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_good, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_bad, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); } } else { item = proto_tree_add_uint_format(udp_tree, hf_udp_checksum, tvb, offset + 6, 2, udph->uh_sum, "Checksum: 0x%04x [unchecked, not all data available]", udph->uh_sum); checksum_tree = proto_item_add_subtree(item, ett_udp_checksum); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_good, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_boolean(checksum_tree, hf_udp_checksum_bad, tvb, offset + 6, 2, FALSE); PROTO_ITEM_SET_GENERATED(item); } /* Skip over header */ offset += 8; pinfo->ptype = PT_UDP; pinfo->srcport = udph->uh_sport; pinfo->destport = udph->uh_dport; tap_queue_packet(udp_tap, pinfo, udph); /* find(or create if needed) the conversation for this udp session */ if (udp_process_info) { conv=find_or_create_conversation(pinfo); udpd=get_udp_conversation_data(conv,pinfo); } if (udpd && ((udpd->fwd && udpd->fwd->command) || (udpd->rev && udpd->rev->command))) { ti = proto_tree_add_text(udp_tree, tvb, offset, 0, "Process Information"); PROTO_ITEM_SET_GENERATED(ti); process_tree = proto_item_add_subtree(ti, ett_udp_process_info); if (udpd->fwd && udpd->fwd->command) { proto_tree_add_uint_format_value(process_tree, hf_udp_proc_dst_uid, tvb, 0, 0, udpd->fwd->process_uid, "%u", udpd->fwd->process_uid); proto_tree_add_uint_format_value(process_tree, hf_udp_proc_dst_pid, tvb, 0, 0, udpd->fwd->process_pid, "%u", udpd->fwd->process_pid); proto_tree_add_string_format_value(process_tree, hf_udp_proc_dst_uname, tvb, 0, 0, udpd->fwd->username, "%s", udpd->fwd->username); proto_tree_add_string_format_value(process_tree, hf_udp_proc_dst_cmd, tvb, 0, 0, udpd->fwd->command, "%s", udpd->fwd->command); } if (udpd->rev->command) { proto_tree_add_uint_format_value(process_tree, hf_udp_proc_src_uid, tvb, 0, 0, udpd->rev->process_uid, "%u", udpd->rev->process_uid); proto_tree_add_uint_format_value(process_tree, hf_udp_proc_src_pid, tvb, 0, 0, udpd->rev->process_pid, "%u", udpd->rev->process_pid); proto_tree_add_string_format_value(process_tree, hf_udp_proc_src_uname, tvb, 0, 0, udpd->rev->username, "%s", udpd->rev->username); proto_tree_add_string_format_value(process_tree, hf_udp_proc_src_cmd, tvb, 0, 0, udpd->rev->command, "%s", udpd->rev->command); } } /* * Call sub-dissectors. * * XXX - should we do this if this is included in an error packet? * It might be nice to see the details of the packet that caused the * ICMP error, but it might not be nice to have the dissector update * state based on it. * Also, we probably don't want to run UDP taps on those packets. * * We definitely don't want to do it for an error packet if there's * nothing left in the packet. */ if (!pinfo->flags.in_error_pkt || tvb_length_remaining(tvb, offset) > 0) decode_udp_ports(tvb, offset, pinfo, tree, udph->uh_sport, udph->uh_dport, udph->uh_ulen); }
void tcp_print(register const u_char *bp, register u_int length, register const u_char *bp2, int fragmented) { register const struct tcphdr *tp; register const struct ip *ip; register u_char flags; register u_int hlen; register char ch; u_int16_t sport, dport, win, urp; u_int32_t seq, ack, thseq, thack; u_int utoval; int threv; #ifdef INET6 register const struct ip6_hdr *ip6; #endif tp = (struct tcphdr *)bp; ip = (struct ip *)bp2; #ifdef INET6 if (IP_V(ip) == 6) ip6 = (struct ip6_hdr *)bp2; else ip6 = NULL; #endif /*INET6*/ ch = '\0'; if (!TTEST(tp->th_dport)) { (void)printf("%s > %s: [|tcp]", ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst)); return; } sport = EXTRACT_16BITS(&tp->th_sport); dport = EXTRACT_16BITS(&tp->th_dport); hlen = TH_OFF(tp) * 4; /* * If data present, header length valid, and NFS port used, * assume NFS. * Pass offset of data plus 4 bytes for RPC TCP msg length * to NFS print routines. */ if (!qflag && hlen >= sizeof(*tp) && hlen <= length && (length - hlen) >= 4) { u_char *fraglenp; u_int32_t fraglen; register struct sunrpc_msg *rp; enum sunrpc_msg_type direction; fraglenp = (u_char *)tp + hlen; if (TTEST2(*fraglenp, 4)) { fraglen = EXTRACT_32BITS(fraglenp) & 0x7FFFFFFF; if (fraglen > (length - hlen) - 4) fraglen = (length - hlen) - 4; rp = (struct sunrpc_msg *)(fraglenp + 4); if (TTEST(rp->rm_direction)) { direction = (enum sunrpc_msg_type)EXTRACT_32BITS(&rp->rm_direction); if (dport == NFS_PORT && direction == SUNRPC_CALL) { nfsreq_print((u_char *)rp, fraglen, (u_char *)ip); return; } if (sport == NFS_PORT && direction == SUNRPC_REPLY) { nfsreply_print((u_char *)rp, fraglen, (u_char *)ip); return; } } } } #ifdef INET6 if (ip6) { if (ip6->ip6_nxt == IPPROTO_TCP) { (void)printf("%s.%s > %s.%s: ", ip6addr_string(&ip6->ip6_src), tcpport_string(sport), ip6addr_string(&ip6->ip6_dst), tcpport_string(dport)); } else { (void)printf("%s > %s: ", tcpport_string(sport), tcpport_string(dport)); } } else #endif /*INET6*/ { if (ip->ip_p == IPPROTO_TCP) { (void)printf("%s.%s > %s.%s: ", ipaddr_string(&ip->ip_src), tcpport_string(sport), ipaddr_string(&ip->ip_dst), tcpport_string(dport)); } else { (void)printf("%s > %s: ", tcpport_string(sport), tcpport_string(dport)); } } if (hlen < sizeof(*tp)) { (void)printf(" tcp %d [bad hdr length %u - too short, < %lu]", length - hlen, hlen, (unsigned long)sizeof(*tp)); return; } TCHECK(*tp); seq = EXTRACT_32BITS(&tp->th_seq); ack = EXTRACT_32BITS(&tp->th_ack); win = EXTRACT_16BITS(&tp->th_win); urp = EXTRACT_16BITS(&tp->th_urp); if (qflag) { (void)printf("tcp %d", length - hlen); if (hlen > length) { (void)printf(" [bad hdr length %u - too long, > %u]", hlen, length); } return; } flags = tp->th_flags; printf("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags)); if (!Sflag && (flags & TH_ACK)) { register struct tcp_seq_hash *th; const void *src, *dst; register int rev; struct tha tha; /* * Find (or record) the initial sequence numbers for * this conversation. (we pick an arbitrary * collating order so there's only one entry for * both directions). */ #ifdef INET6 rev = 0; if (ip6) { src = &ip6->ip6_src; dst = &ip6->ip6_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (memcmp(src, dst, sizeof ip6->ip6_dst) > 0) rev = 1; } if (rev) { memcpy(&tha.src, dst, sizeof ip6->ip6_dst); memcpy(&tha.dst, src, sizeof ip6->ip6_src); tha.port = dport << 16 | sport; } else { memcpy(&tha.dst, dst, sizeof ip6->ip6_dst); memcpy(&tha.src, src, sizeof ip6->ip6_src); tha.port = sport << 16 | dport; } } else { /* * Zero out the tha structure; the src and dst * fields are big enough to hold an IPv6 * address, but we only have IPv4 addresses * and thus must clear out the remaining 124 * bits. * * XXX - should we just clear those bytes after * copying the IPv4 addresses, rather than * zeroing out the entire structure and then * overwriting some of the zeroes? * * XXX - this could fail if we see TCP packets * with an IPv6 address with the lower 124 bits * all zero and also see TCP packes with an * IPv4 address with the same 32 bits as the * upper 32 bits of the IPv6 address in question. * Can that happen? Is it likely enough to be * an issue? */ memset(&tha, 0, sizeof(tha)); src = &ip->ip_src; dst = &ip->ip_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (memcmp(src, dst, sizeof ip->ip_dst) > 0) rev = 1; } if (rev) { memcpy(&tha.src, dst, sizeof ip->ip_dst); memcpy(&tha.dst, src, sizeof ip->ip_src); tha.port = dport << 16 | sport; } else { memcpy(&tha.dst, dst, sizeof ip->ip_dst); memcpy(&tha.src, src, sizeof ip->ip_src); tha.port = sport << 16 | dport; } } #else rev = 0; src = &ip->ip_src; dst = &ip->ip_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (memcmp(src, dst, sizeof ip->ip_dst) > 0) rev = 1; } if (rev) { memcpy(&tha.src, dst, sizeof ip->ip_dst); memcpy(&tha.dst, src, sizeof ip->ip_src); tha.port = dport << 16 | sport; } else { memcpy(&tha.dst, dst, sizeof ip->ip_dst); memcpy(&tha.src, src, sizeof ip->ip_src); tha.port = sport << 16 | dport; } #endif threv = rev; for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (memcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr)) == 0) break; if (!th->nxt || (flags & TH_SYN)) { /* didn't find it or new conversation */ if (th->nxt == NULL) { th->nxt = (struct tcp_seq_hash *) calloc(1, sizeof(*th)); if (th->nxt == NULL) error("tcp_print: calloc"); } th->addr = tha; if (rev) th->ack = seq, th->seq = ack - 1; else th->seq = seq, th->ack = ack - 1; } else { if (rev) seq -= th->ack, ack -= th->seq; else seq -= th->seq, ack -= th->ack; } thseq = th->seq; thack = th->ack; } else { /*fool gcc*/ thseq = thack = threv = 0; } if (hlen > length) { (void)printf(" [bad hdr length %u - too long, > %u]", hlen, length); return; } if (IP_V(ip) == 4 && vflag && !Kflag && !fragmented) { u_int16_t sum, tcp_sum; if (TTEST2(tp->th_sport, length)) { sum = tcp_cksum(ip, tp, length); (void)printf(", cksum 0x%04x",EXTRACT_16BITS(&tp->th_sum)); if (sum != 0) { tcp_sum = EXTRACT_16BITS(&tp->th_sum); (void)printf(" (incorrect -> 0x%04x)",in_cksum_shouldbe(tcp_sum, sum)); } else (void)printf(" (correct)"); } } #ifdef INET6 if (IP_V(ip) == 6 && ip6->ip6_plen && vflag && !Kflag && !fragmented) { u_int16_t sum,tcp_sum; if (TTEST2(tp->th_sport, length)) { sum = nextproto6_cksum(ip6, (u_short *)tp, length, IPPROTO_TCP); (void)printf(", cksum 0x%04x",EXTRACT_16BITS(&tp->th_sum)); if (sum != 0) { tcp_sum = EXTRACT_16BITS(&tp->th_sum); (void)printf(" (incorrect -> 0x%04x)",in_cksum_shouldbe(tcp_sum, sum)); } else (void)printf(" (correct)"); } } #endif length -= hlen; if (vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) { (void)printf(", seq %u", seq); if (length > 0) { (void)printf(":%u", seq + length); } } if (flags & TH_ACK) { (void)printf(", ack %u", ack); } (void)printf(", win %d", win); if (flags & TH_URG) (void)printf(", urg %d", urp); /* * Handle any options. */ if (hlen > sizeof(*tp)) { register const u_char *cp; register u_int i, opt, datalen; register u_int len; hlen -= sizeof(*tp); cp = (const u_char *)tp + sizeof(*tp); printf(", options ["); while (hlen > 0) { if (ch != '\0') putchar(ch); TCHECK(*cp); opt = *cp++; if (ZEROLENOPT(opt)) len = 1; else { TCHECK(*cp); len = *cp++; /* total including type, len */ if (len < 2 || len > hlen) goto bad; --hlen; /* account for length byte */ } --hlen; /* account for type byte */ datalen = 0; /* Bail if "l" bytes of data are not left or were not captured */ #define LENCHECK(l) { if ((l) > hlen) goto bad; TCHECK2(*cp, l); } printf("%s", tok2str(tcp_option_values, "Unknown Option %u", opt)); switch (opt) { case TCPOPT_MAXSEG: datalen = 2; LENCHECK(datalen); (void)printf(" %u", EXTRACT_16BITS(cp)); break; case TCPOPT_WSCALE: datalen = 1; LENCHECK(datalen); (void)printf(" %u", *cp); break; case TCPOPT_SACK: datalen = len - 2; if (datalen % 8 != 0) { (void)printf("malformed sack"); } else { u_int32_t s, e; (void)printf(" %d ", datalen / 8); for (i = 0; i < datalen; i += 8) { LENCHECK(i + 4); s = EXTRACT_32BITS(cp + i); LENCHECK(i + 8); e = EXTRACT_32BITS(cp + i + 4); if (threv) { s -= thseq; e -= thseq; } else { s -= thack; e -= thack; } (void)printf("{%u:%u}", s, e); } } break; case TCPOPT_CC: case TCPOPT_CCNEW: case TCPOPT_CCECHO: case TCPOPT_ECHO: case TCPOPT_ECHOREPLY: /* * those options share their semantics. * fall through */ datalen = 4; LENCHECK(datalen); (void)printf(" %u", EXTRACT_32BITS(cp)); break; case TCPOPT_TIMESTAMP: datalen = 8; LENCHECK(datalen); (void)printf(" val %u ecr %u", EXTRACT_32BITS(cp), EXTRACT_32BITS(cp + 4)); break; case TCPOPT_SIGNATURE: datalen = TCP_SIGLEN; LENCHECK(datalen); #ifdef HAVE_LIBCRYPTO switch (tcp_verify_signature(ip, tp, bp + TH_OFF(tp) * 4, length, cp)) { case SIGNATURE_VALID: (void)printf("valid"); break; case SIGNATURE_INVALID: (void)printf("invalid"); break; case CANT_CHECK_SIGNATURE: (void)printf("can't check - "); for (i = 0; i < TCP_SIGLEN; ++i) (void)printf("%02x", cp[i]); break; } #else for (i = 0; i < TCP_SIGLEN; ++i) (void)printf("%02x", cp[i]); #endif break; case TCPOPT_AUTH: (void)printf("keyid %d", *cp++); datalen = len - 3; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } break; case TCPOPT_EOL: case TCPOPT_NOP: case TCPOPT_SACKOK: /* * Nothing interesting. * fall through */ break; case TCPOPT_UTO: datalen = 2; LENCHECK(datalen); utoval = EXTRACT_16BITS(cp); (void)printf("0x%x", utoval); if (utoval & 0x0001) utoval = (utoval >> 1) * 60; else utoval >>= 1; (void)printf(" %u", utoval); break; default: datalen = len - 2; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } break; } /* Account for data printed */ cp += datalen; hlen -= datalen; /* Check specification against observed length */ ++datalen; /* option octet */ if (!ZEROLENOPT(opt)) ++datalen; /* size octet */ if (datalen != len) (void)printf("[len %d]", len); ch = ','; if (opt == TCPOPT_EOL) break; } putchar(']'); }
void dissect_nhrp_hdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *pOffset, gint *pMandLen, gint *pExtLen, oui_info_t **pOuiInfo, e_nhrp_hdr *hdr) { gint offset = *pOffset; const gchar *pro_type_str; guint total_len = tvb_reported_length(tvb); guint16 ipcsum, rx_chksum; proto_item *nhrp_tree_item = NULL; proto_tree *nhrp_tree = NULL; proto_item *shtl_tree_item = NULL; proto_tree *shtl_tree = NULL; proto_item *sstl_tree_item = NULL; proto_tree *sstl_tree = NULL; proto_item *ti; nhrp_tree_item = proto_tree_add_text(tree, tvb, offset, 20, "NHRP Fixed Header"); nhrp_tree = proto_item_add_subtree(nhrp_tree_item, ett_nhrp_hdr); hdr->ar_pktsz = tvb_get_ntohs(tvb, 10); if (total_len > hdr->ar_pktsz) { total_len = hdr->ar_pktsz; } hdr->ar_afn = tvb_get_ntohs(tvb, offset); proto_tree_add_item(nhrp_tree, hf_nhrp_hdr_afn, tvb, offset, 2, FALSE); offset += 2; hdr->ar_pro_type = tvb_get_ntohs(tvb, offset); if (hdr->ar_pro_type <= 0xFF) { /* It's an NLPID */ pro_type_str = val_to_str(hdr->ar_pro_type, nlpid_vals, "Unknown NLPID"); } else if (hdr->ar_pro_type <= 0x3FF) { /* Reserved for future use by the IETF */ pro_type_str = "Reserved for future use by the IETF"; } else if (hdr->ar_pro_type <= 0x04FF) { /* Allocated for use by the ATM Forum */ pro_type_str = "Allocated for use by the ATM Forum"; } else if (hdr->ar_pro_type <= 0x05FF) { /* Experimental/Local use */ pro_type_str = "Experimental/Local use"; } else { pro_type_str = val_to_str(hdr->ar_pro_type, etype_vals, "Unknown Ethertype"); } proto_tree_add_uint_format(nhrp_tree, hf_nhrp_hdr_pro_type, tvb, offset, 2, hdr->ar_pro_type, "Protocol Type (short form): %s (0x%04x)", pro_type_str, hdr->ar_pro_type); offset += 2; if (hdr->ar_pro_type == NLPID_SNAP) { /* * The long form protocol type is a SNAP OUI and PID. */ hdr->ar_pro_type_oui = tvb_get_ntoh24(tvb, offset); proto_tree_add_uint(nhrp_tree, hf_nhrp_hdr_pro_snap_oui, tvb, offset, 3, hdr->ar_pro_type_oui); offset += 3; hdr->ar_pro_type_pid = tvb_get_ntohs(tvb, offset); *pOuiInfo = get_snap_oui_info(hdr->ar_pro_type_oui); if (*pOuiInfo != NULL) { proto_tree_add_uint(nhrp_tree, *(*pOuiInfo)->field_info->p_id, tvb, offset, 2, hdr->ar_pro_type_pid); } else { proto_tree_add_uint(nhrp_tree, hf_nhrp_hdr_pro_snap_pid, tvb, offset, 2, hdr->ar_pro_type_pid); } } else { /* * XXX - we should check that this is zero, as RFC 2332 * says it should be zero. */ proto_tree_add_text(nhrp_tree, tvb, offset, 5, "Protocol Type (long form): %s", tvb_bytes_to_str(tvb, offset, 5)); offset += 5; } proto_tree_add_item(nhrp_tree, hf_nhrp_hdr_hopcnt, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(nhrp_tree, hf_nhrp_hdr_pktsz, tvb, offset, 2, FALSE); offset += 2; rx_chksum = tvb_get_ntohs(tvb, offset); if (tvb_bytes_exist(tvb, 0, total_len)) { ipcsum = nhrp_checksum(tvb_get_ptr(tvb, 0, total_len), total_len); if (ipcsum == 0) { proto_tree_add_uint_format(nhrp_tree, hf_nhrp_hdr_chksum, tvb, offset, 2, rx_chksum, "NHRP Packet checksum: 0x%04x [correct]", rx_chksum); } else { proto_tree_add_uint_format(nhrp_tree, hf_nhrp_hdr_chksum, tvb, offset, 2, rx_chksum, "NHRP Packet checksum: 0x%04x [incorrect, should be 0x%04x]", rx_chksum, in_cksum_shouldbe(rx_chksum, ipcsum)); } } else { proto_tree_add_uint_format(nhrp_tree, hf_nhrp_hdr_chksum, tvb, offset, 2, rx_chksum, "NHRP Packet checksum: 0x%04x [not all data available]", rx_chksum); } offset += 2; hdr->ar_extoff = tvb_get_ntohs(tvb, offset); ti = proto_tree_add_item(nhrp_tree, hf_nhrp_hdr_extoff, tvb, offset, 2, FALSE); if (hdr->ar_extoff != 0 && hdr->ar_extoff < 20) { expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR, "Extension offset is less than the fixed header length"); } offset += 2; hdr->ar_op_version = tvb_get_guint8(tvb, offset); proto_tree_add_uint_format(nhrp_tree, hf_nhrp_hdr_version, tvb, offset, 1, hdr->ar_op_version, "Version : %u (%s)", hdr->ar_op_version, (hdr->ar_op_version == 1) ? "NHRP - rfc2332" : "Unknown"); offset += 1; proto_tree_add_item(nhrp_tree, hf_nhrp_hdr_op_type, tvb, offset, 1, FALSE); offset += 1; hdr->ar_shtl = tvb_get_guint8(tvb, offset); shtl_tree_item = proto_tree_add_uint_format(nhrp_tree, hf_nhrp_hdr_shtl, tvb, offset, 1, hdr->ar_shtl, "Source Address Type/Len: %s/%u", val_to_str(NHRP_SHTL_TYPE(hdr->ar_shtl), nhrp_shtl_type_vals, "Unknown Type"), NHRP_SHTL_LEN(hdr->ar_shtl)); shtl_tree = proto_item_add_subtree(shtl_tree_item, ett_nhrp_hdr_shtl); proto_tree_add_item(shtl_tree, hf_nhrp_hdr_shtl_type, tvb, offset, 1, FALSE); proto_tree_add_item(shtl_tree, hf_nhrp_hdr_shtl_len, tvb, offset, 1, FALSE); offset += 1; hdr->ar_sstl = tvb_get_guint8(tvb, offset); sstl_tree_item = proto_tree_add_uint_format(nhrp_tree, hf_nhrp_hdr_sstl, tvb, offset, 1, hdr->ar_sstl, "Source SubAddress Type/Len: %s/%u", val_to_str(NHRP_SHTL_TYPE(hdr->ar_sstl), nhrp_shtl_type_vals, "Unknown Type"), NHRP_SHTL_LEN(hdr->ar_sstl)); sstl_tree = proto_item_add_subtree(sstl_tree_item, ett_nhrp_hdr_sstl); proto_tree_add_item(sstl_tree, hf_nhrp_hdr_sstl_type, tvb, offset, 1, FALSE); proto_tree_add_item(sstl_tree, hf_nhrp_hdr_sstl_len, tvb, offset, 1, FALSE); offset += 1; *pOffset = offset; if (hdr->ar_extoff != 0) { if (hdr->ar_extoff >= 20) { *pMandLen = hdr->ar_extoff - 20; *pExtLen = total_len - hdr->ar_extoff; } else { /* Error */ *pMandLen = 0; *pExtLen = 0; } } else { if (total_len >= 20) *pMandLen = total_len - 20; else { /* "Can't happen" - we would have thrown an exception */ *pMandLen = 0; } *pExtLen = 0; } }
void tcp_print(netdissect_options *ndo, const u_char *bp, u_int length, const u_char *bp2, int fragmented) { const struct tcphdr *tp; const struct ip *ip; u_char flags; u_int hlen; char ch; uint16_t sport, dport, win, urp; uint32_t seq, ack, thseq, thack; u_int utoval; uint16_t magic; int rev; const struct ip6_hdr *ip6; ndo->ndo_protocol = "tcp"; tp = (const struct tcphdr *)bp; ip = (const struct ip *)bp2; if (IP_V(ip) == 6) ip6 = (const struct ip6_hdr *)bp2; else ip6 = NULL; ch = '\0'; if (!ND_TTEST_2(tp->th_dport)) { if (ip6) { ND_PRINT("%s > %s:", ip6addr_string(ndo, ip6->ip6_src), ip6addr_string(ndo, ip6->ip6_dst)); } else { ND_PRINT("%s > %s:", ipaddr_string(ndo, ip->ip_src), ipaddr_string(ndo, ip->ip_dst)); } nd_print_trunc(ndo); return; } sport = GET_BE_U_2(tp->th_sport); dport = GET_BE_U_2(tp->th_dport); if (ip6) { if (GET_U_1(ip6->ip6_nxt) == IPPROTO_TCP) { ND_PRINT("%s.%s > %s.%s: ", ip6addr_string(ndo, ip6->ip6_src), tcpport_string(ndo, sport), ip6addr_string(ndo, ip6->ip6_dst), tcpport_string(ndo, dport)); } else { ND_PRINT("%s > %s: ", tcpport_string(ndo, sport), tcpport_string(ndo, dport)); } } else { if (GET_U_1(ip->ip_p) == IPPROTO_TCP) { ND_PRINT("%s.%s > %s.%s: ", ipaddr_string(ndo, ip->ip_src), tcpport_string(ndo, sport), ipaddr_string(ndo, ip->ip_dst), tcpport_string(ndo, dport)); } else { ND_PRINT("%s > %s: ", tcpport_string(ndo, sport), tcpport_string(ndo, dport)); } } ND_TCHECK_SIZE(tp); hlen = TH_OFF(tp) * 4; if (hlen < sizeof(*tp)) { ND_PRINT(" tcp %u [bad hdr length %u - too short, < %lu]", length - hlen, hlen, (unsigned long)sizeof(*tp)); return; } seq = GET_BE_U_4(tp->th_seq); ack = GET_BE_U_4(tp->th_ack); win = GET_BE_U_2(tp->th_win); urp = GET_BE_U_2(tp->th_urp); if (ndo->ndo_qflag) { ND_PRINT("tcp %u", length - hlen); if (hlen > length) { ND_PRINT(" [bad hdr length %u - too long, > %u]", hlen, length); } return; } flags = GET_U_1(tp->th_flags); ND_PRINT("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags)); if (!ndo->ndo_Sflag && (flags & TH_ACK)) { /* * Find (or record) the initial sequence numbers for * this conversation. (we pick an arbitrary * collating order so there's only one entry for * both directions). */ rev = 0; if (ip6) { struct tcp_seq_hash6 *th; struct tcp_seq_hash6 *tcp_seq_hash; const void *src, *dst; struct tha6 tha; tcp_seq_hash = tcp_seq_hash6; src = (const void *)ip6->ip6_src; dst = (const void *)ip6->ip6_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (UNALIGNED_MEMCMP(src, dst, sizeof(ip6->ip6_dst)) > 0) rev = 1; } if (rev) { UNALIGNED_MEMCPY(&tha.src, dst, sizeof(ip6->ip6_dst)); UNALIGNED_MEMCPY(&tha.dst, src, sizeof(ip6->ip6_src)); tha.port = ((u_int)dport) << 16 | sport; } else { UNALIGNED_MEMCPY(&tha.dst, dst, sizeof(ip6->ip6_dst)); UNALIGNED_MEMCPY(&tha.src, src, sizeof(ip6->ip6_src)); tha.port = ((u_int)sport) << 16 | dport; } for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (memcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr)) == 0) break; if (!th->nxt || (flags & TH_SYN)) { /* didn't find it or new conversation */ /* calloc() return used by the 'tcp_seq_hash6' hash table: do not free() */ if (th->nxt == NULL) { th->nxt = (struct tcp_seq_hash6 *) calloc(1, sizeof(*th)); if (th->nxt == NULL) (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "tcp_print: calloc"); } th->addr = tha; if (rev) th->ack = seq, th->seq = ack - 1; else th->seq = seq, th->ack = ack - 1; } else { if (rev) seq -= th->ack, ack -= th->seq; else seq -= th->seq, ack -= th->ack; } thseq = th->seq; thack = th->ack; } else { struct tcp_seq_hash *th; struct tcp_seq_hash *tcp_seq_hash; struct tha tha; tcp_seq_hash = tcp_seq_hash4; if (sport > dport) rev = 1; else if (sport == dport) { if (UNALIGNED_MEMCMP(ip->ip_src, ip->ip_dst, sizeof(ip->ip_dst)) > 0) rev = 1; } if (rev) { UNALIGNED_MEMCPY(&tha.src, ip->ip_dst, sizeof(ip->ip_dst)); UNALIGNED_MEMCPY(&tha.dst, ip->ip_src, sizeof(ip->ip_src)); tha.port = ((u_int)dport) << 16 | sport; } else { UNALIGNED_MEMCPY(&tha.dst, ip->ip_dst, sizeof(ip->ip_dst)); UNALIGNED_MEMCPY(&tha.src, ip->ip_src, sizeof(ip->ip_src)); tha.port = ((u_int)sport) << 16 | dport; } for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (memcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr)) == 0) break; if (!th->nxt || (flags & TH_SYN)) { /* didn't find it or new conversation */ /* calloc() return used by the 'tcp_seq_hash4' hash table: do not free() */ if (th->nxt == NULL) { th->nxt = (struct tcp_seq_hash *) calloc(1, sizeof(*th)); if (th->nxt == NULL) (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "tcp_print: calloc"); } th->addr = tha; if (rev) th->ack = seq, th->seq = ack - 1; else th->seq = seq, th->ack = ack - 1; } else { if (rev) seq -= th->ack, ack -= th->seq; else seq -= th->seq, ack -= th->ack; } thseq = th->seq; thack = th->ack; } } else { /*fool gcc*/ thseq = thack = rev = 0; } if (hlen > length) { ND_PRINT(" [bad hdr length %u - too long, > %u]", hlen, length); return; } if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented) { /* Check the checksum, if possible. */ uint16_t sum, tcp_sum; if (IP_V(ip) == 4) { if (ND_TTEST_LEN(tp->th_sport, length)) { sum = tcp_cksum(ndo, ip, tp, length); tcp_sum = GET_BE_U_2(tp->th_sum); ND_PRINT(", cksum 0x%04x", tcp_sum); if (sum != 0) ND_PRINT(" (incorrect -> 0x%04x)", in_cksum_shouldbe(tcp_sum, sum)); else ND_PRINT(" (correct)"); } } else if (IP_V(ip) == 6 && ip6->ip6_plen) { if (ND_TTEST_LEN(tp->th_sport, length)) { sum = tcp6_cksum(ndo, ip6, tp, length); tcp_sum = GET_BE_U_2(tp->th_sum); ND_PRINT(", cksum 0x%04x", tcp_sum); if (sum != 0) ND_PRINT(" (incorrect -> 0x%04x)", in_cksum_shouldbe(tcp_sum, sum)); else ND_PRINT(" (correct)"); } } } length -= hlen; if (ndo->ndo_vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) { ND_PRINT(", seq %u", seq); if (length > 0) { ND_PRINT(":%u", seq + length); } } if (flags & TH_ACK) { ND_PRINT(", ack %u", ack); } ND_PRINT(", win %u", win); if (flags & TH_URG) ND_PRINT(", urg %u", urp); /* * Handle any options. */ if (hlen > sizeof(*tp)) { const u_char *cp; u_int i, opt, datalen; u_int len; hlen -= sizeof(*tp); cp = (const u_char *)tp + sizeof(*tp); ND_PRINT(", options ["); while (hlen > 0) { if (ch != '\0') ND_PRINT("%c", ch); ND_TCHECK_1(cp); opt = GET_U_1(cp); cp++; if (ZEROLENOPT(opt)) len = 1; else { ND_TCHECK_1(cp); len = GET_U_1(cp); cp++; /* total including type, len */ if (len < 2 || len > hlen) goto bad; --hlen; /* account for length byte */ } --hlen; /* account for type byte */ datalen = 0; /* Bail if "l" bytes of data are not left or were not captured */ #define LENCHECK(l) { if ((l) > hlen) goto bad; ND_TCHECK_LEN(cp, l); } ND_PRINT("%s", tok2str(tcp_option_values, "unknown-%u", opt)); switch (opt) { case TCPOPT_MAXSEG: datalen = 2; LENCHECK(datalen); ND_PRINT(" %u", GET_BE_U_2(cp)); break; case TCPOPT_WSCALE: datalen = 1; LENCHECK(datalen); ND_PRINT(" %u", GET_U_1(cp)); break; case TCPOPT_SACK: datalen = len - 2; if (datalen % 8 != 0) { ND_PRINT(" invalid sack"); } else { uint32_t s, e; ND_PRINT(" %u ", datalen / 8); for (i = 0; i < datalen; i += 8) { LENCHECK(i + 4); s = GET_BE_U_4(cp + i); LENCHECK(i + 8); e = GET_BE_U_4(cp + i + 4); if (rev) { s -= thseq; e -= thseq; } else { s -= thack; e -= thack; } ND_PRINT("{%u:%u}", s, e); } } break; case TCPOPT_CC: case TCPOPT_CCNEW: case TCPOPT_CCECHO: case TCPOPT_ECHO: case TCPOPT_ECHOREPLY: /* * those options share their semantics. * fall through */ datalen = 4; LENCHECK(datalen); ND_PRINT(" %u", GET_BE_U_4(cp)); break; case TCPOPT_TIMESTAMP: datalen = 8; LENCHECK(datalen); ND_PRINT(" val %u ecr %u", GET_BE_U_4(cp), GET_BE_U_4(cp + 4)); break; case TCPOPT_SIGNATURE: datalen = TCP_SIGLEN; LENCHECK(datalen); ND_PRINT(" "); #ifdef HAVE_LIBCRYPTO switch (tcp_verify_signature(ndo, ip, tp, bp + TH_OFF(tp) * 4, length, cp)) { case SIGNATURE_VALID: ND_PRINT("valid"); break; case SIGNATURE_INVALID: nd_print_invalid(ndo); break; case CANT_CHECK_SIGNATURE: ND_PRINT("can't check - "); for (i = 0; i < TCP_SIGLEN; ++i) ND_PRINT("%02x", GET_U_1(cp + i)); break; } #else for (i = 0; i < TCP_SIGLEN; ++i) ND_PRINT("%02x", GET_U_1(cp + i)); #endif break; case TCPOPT_SCPS: datalen = 2; LENCHECK(datalen); ND_PRINT(" cap %02x id %u", GET_U_1(cp), GET_U_1(cp + 1)); break; case TCPOPT_TCPAO: datalen = len - 2; /* RFC 5925 Section 2.2: * "The Length value MUST be greater than or equal to 4." * (This includes the Kind and Length fields already processed * at this point.) */ if (datalen < 2) { nd_print_invalid(ndo); } else { LENCHECK(1); ND_PRINT(" keyid %u", GET_U_1(cp)); LENCHECK(2); ND_PRINT(" rnextkeyid %u", GET_U_1(cp + 1)); if (datalen > 2) { ND_PRINT(" mac 0x"); for (i = 2; i < datalen; i++) { LENCHECK(i + 1); ND_PRINT("%02x", GET_U_1(cp + i)); } } } break; case TCPOPT_EOL: case TCPOPT_NOP: case TCPOPT_SACKOK: /* * Nothing interesting. * fall through */ break; case TCPOPT_UTO: datalen = 2; LENCHECK(datalen); utoval = GET_BE_U_2(cp); ND_PRINT(" 0x%x", utoval); if (utoval & 0x0001) utoval = (utoval >> 1) * 60; else utoval >>= 1; ND_PRINT(" %u", utoval); break; case TCPOPT_MPTCP: datalen = len - 2; LENCHECK(datalen); if (!mptcp_print(ndo, cp-2, len, flags)) goto bad; break; case TCPOPT_FASTOPEN: datalen = len - 2; LENCHECK(datalen); ND_PRINT(" "); print_tcp_fastopen_option(ndo, cp, datalen, FALSE); break; case TCPOPT_EXPERIMENT2: datalen = len - 2; LENCHECK(datalen); if (datalen < 2) goto bad; /* RFC6994 */ magic = GET_BE_U_2(cp); ND_PRINT("-"); switch(magic) { case 0xf989: /* TCP Fast Open RFC 7413 */ print_tcp_fastopen_option(ndo, cp + 2, datalen - 2, TRUE); break; default: /* Unknown magic number */ ND_PRINT("%04x", magic); break; } break; default: datalen = len - 2; if (datalen) ND_PRINT(" 0x"); for (i = 0; i < datalen; ++i) { LENCHECK(i + 1); ND_PRINT("%02x", GET_U_1(cp + i)); } break; } /* Account for data printed */ cp += datalen; hlen -= datalen; /* Check specification against observed length */ ++datalen; /* option octet */ if (!ZEROLENOPT(opt)) ++datalen; /* size octet */ if (datalen != len) ND_PRINT("[len %u]", len); ch = ','; if (opt == TCPOPT_EOL) break; } ND_PRINT("]"); }
/** * dccp_print - show dccp packet * @bp - beginning of dccp packet * @data2 - beginning of enclosing * @len - length of ip packet */ void dccp_print(netdissect_options *ndo, const u_char *bp, const u_char *data2, u_int len) { const struct dccp_hdr *dh; const struct ip *ip; const struct ip6_hdr *ip6; const u_char *cp; u_short sport, dport; u_int hlen; u_int fixed_hdrlen; uint8_t dccph_type; ndo->ndo_protocol = "dccp"; dh = (const struct dccp_hdr *)bp; ip = (const struct ip *)data2; if (IP_V(ip) == 6) ip6 = (const struct ip6_hdr *)data2; else ip6 = NULL; /* make sure we have enough data to look at the X bit */ cp = (const u_char *)(dh + 1); if (cp > ndo->ndo_snapend) goto trunc; if (len < sizeof(struct dccp_hdr)) { ND_PRINT("truncated-dccp - %u bytes missing!", (u_int)sizeof(struct dccp_hdr) - len); return; } /* get the length of the generic header */ fixed_hdrlen = dccp_basic_hdr_len(ndo, dh); if (len < fixed_hdrlen) { ND_PRINT("truncated-dccp - %u bytes missing!", fixed_hdrlen - len); return; } ND_TCHECK_LEN(dh, fixed_hdrlen); sport = GET_BE_U_2(dh->dccph_sport); dport = GET_BE_U_2(dh->dccph_dport); hlen = GET_U_1(dh->dccph_doff) * 4; if (ip6) { ND_PRINT("%s.%u > %s.%u: ", ip6addr_string(ndo, ip6->ip6_src), sport, ip6addr_string(ndo, ip6->ip6_dst), dport); } else { ND_PRINT("%s.%u > %s.%u: ", ipaddr_string(ndo, ip->ip_src), sport, ipaddr_string(ndo, ip->ip_dst), dport); } ND_PRINT("DCCP"); if (ndo->ndo_qflag) { ND_PRINT(" %u", len - hlen); if (hlen > len) { ND_PRINT(" [bad hdr length %u - too long, > %u]", hlen, len); } return; } /* other variables in generic header */ if (ndo->ndo_vflag) { ND_PRINT(" (CCVal %u, CsCov %u, ", DCCPH_CCVAL(dh), DCCPH_CSCOV(dh)); } /* checksum calculation */ if (ndo->ndo_vflag && ND_TTEST_LEN(bp, len)) { uint16_t sum = 0, dccp_sum; dccp_sum = GET_BE_U_2(dh->dccph_checksum); ND_PRINT("cksum 0x%04x ", dccp_sum); if (IP_V(ip) == 4) sum = dccp_cksum(ndo, ip, dh, len); else if (IP_V(ip) == 6) sum = dccp6_cksum(ndo, ip6, dh, len); if (sum != 0) ND_PRINT("(incorrect -> 0x%04x)",in_cksum_shouldbe(dccp_sum, sum)); else ND_PRINT("(correct)"); } if (ndo->ndo_vflag) ND_PRINT(")"); ND_PRINT(" "); dccph_type = DCCPH_TYPE(dh); switch (dccph_type) { case DCCP_PKT_REQUEST: { const struct dccp_hdr_request *dhr = (const struct dccp_hdr_request *)(bp + fixed_hdrlen); fixed_hdrlen += 4; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_TCHECK_SIZE(dhr); ND_PRINT("%s (service=%u) ", tok2str(dccp_pkt_type_str, "", dccph_type), GET_BE_U_4(dhr->dccph_req_service)); break; } case DCCP_PKT_RESPONSE: { const struct dccp_hdr_response *dhr = (const struct dccp_hdr_response *)(bp + fixed_hdrlen); fixed_hdrlen += 12; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_TCHECK_SIZE(dhr); ND_PRINT("%s (service=%u) ", tok2str(dccp_pkt_type_str, "", dccph_type), GET_BE_U_4(dhr->dccph_resp_service)); break; } case DCCP_PKT_DATA: ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_ACK: { fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; } case DCCP_PKT_DATAACK: { fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; } case DCCP_PKT_CLOSEREQ: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_CLOSE: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_RESET: { const struct dccp_hdr_reset *dhr = (const struct dccp_hdr_reset *)(bp + fixed_hdrlen); fixed_hdrlen += 12; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_TCHECK_SIZE(dhr); ND_PRINT("%s (code=%s) ", tok2str(dccp_pkt_type_str, "", dccph_type), dccp_reset_code(GET_U_1(dhr->dccph_reset_code))); break; } case DCCP_PKT_SYNC: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_SYNCACK: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; default: ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "unknown-type-%u", dccph_type)); break; } if ((DCCPH_TYPE(dh) != DCCP_PKT_DATA) && (DCCPH_TYPE(dh) != DCCP_PKT_REQUEST)) dccp_print_ack_no(ndo, bp); if (ndo->ndo_vflag < 2) return; ND_PRINT("seq %" PRIu64, dccp_seqno(ndo, bp)); /* process options */ if (hlen > fixed_hdrlen){ u_int optlen; cp = bp + fixed_hdrlen; ND_PRINT(" <"); hlen -= fixed_hdrlen; while(1){ optlen = dccp_print_option(ndo, cp, hlen); if (!optlen) break; if (hlen <= optlen) break; hlen -= optlen; cp += optlen; ND_PRINT(", "); } ND_PRINT(">"); } return; trunc: nd_print_trunc(ndo); return; }
void tcp_print(const u_char *bp, u_int length, const u_char *bp2) { const struct tcphdr *tp; const struct ip *ip; u_char flags; int hlen; char ch; struct tcp_seq_hash *th = NULL; int rev = 0; u_int16_t sport, dport, win, urp; tcp_seq seq, ack; #ifdef INET6 const struct ip6_hdr *ip6; #endif tp = (struct tcphdr *)bp; switch (((struct ip *)bp2)->ip_v) { case 4: ip = (struct ip *)bp2; #ifdef INET6 ip6 = NULL; #endif break; #ifdef INET6 case 6: ip = NULL; ip6 = (struct ip6_hdr *)bp2; break; #endif default: (void)printf("invalid ip version"); return; } ch = '\0'; if (length < sizeof(*tp)) { (void)printf("truncated-tcp %u", length); return; } if (!TTEST(tp->th_dport)) { #ifdef INET6 if (ip6) { (void)printf("%s > %s: [|tcp]", ip6addr_string(&ip6->ip6_src), ip6addr_string(&ip6->ip6_dst)); } else #endif /*INET6*/ { (void)printf("%s > %s: [|tcp]", ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst)); } return; } sport = ntohs(tp->th_sport); dport = ntohs(tp->th_dport); #ifdef INET6 if (ip6) { if (ip6->ip6_nxt == IPPROTO_TCP) { (void)printf("%s.%s > %s.%s: ", ip6addr_string(&ip6->ip6_src), tcpport_string(sport), ip6addr_string(&ip6->ip6_dst), tcpport_string(dport)); } else { (void)printf("%s > %s: ", tcpport_string(sport), tcpport_string(dport)); } } else #endif /*INET6*/ { if (ip->ip_p == IPPROTO_TCP) { (void)printf("%s.%s > %s.%s: ", ipaddr_string(&ip->ip_src), tcpport_string(sport), ipaddr_string(&ip->ip_dst), tcpport_string(dport)); } else { (void)printf("%s > %s: ", tcpport_string(sport), tcpport_string(dport)); } } if (!qflag && TTEST(tp->th_seq) && !TTEST(tp->th_ack)) (void)printf("%u ", ntohl(tp->th_seq)); TCHECK(*tp); seq = ntohl(tp->th_seq); ack = ntohl(tp->th_ack); win = ntohs(tp->th_win); urp = ntohs(tp->th_urp); hlen = tp->th_off * 4; if (qflag) { (void)printf("tcp %d", length - tp->th_off * 4); return; } else if (packettype != PT_TCP) { /* * If data present and NFS port used, assume NFS. * Pass offset of data plus 4 bytes for RPC TCP msg length * to NFS print routines. */ u_int len = length - hlen; if ((u_char *)tp + 4 + sizeof(struct rpc_msg) <= snapend && dport == NFS_PORT) { nfsreq_print((u_char *)tp + hlen + 4, len, bp2); return; } else if ((u_char *)tp + 4 + sizeof(struct rpc_msg) <= snapend && sport == NFS_PORT) { nfsreply_print((u_char *)tp + hlen + 4, len, bp2); return; } } if ((flags = tp->th_flags) & (TH_SYN|TH_FIN|TH_RST|TH_PUSH| TH_ECNECHO|TH_CWR)) { if (flags & TH_SYN) putchar('S'); if (flags & TH_FIN) putchar('F'); if (flags & TH_RST) putchar('R'); if (flags & TH_PUSH) putchar('P'); if (flags & TH_CWR) putchar('W'); /* congestion _W_indow reduced (ECN) */ if (flags & TH_ECNECHO) putchar('E'); /* ecn _E_cho sent (ECN) */ } else putchar('.'); if (!Sflag && (flags & TH_ACK)) { struct tha tha; /* * Find (or record) the initial sequence numbers for * this conversation. (we pick an arbitrary * collating order so there's only one entry for * both directions). */ #ifdef INET6 bzero(&tha, sizeof(tha)); rev = 0; if (ip6) { if (sport > dport) { rev = 1; } else if (sport == dport) { int i; for (i = 0; i < 4; i++) { if (((u_int32_t *)(&ip6->ip6_src))[i] > ((u_int32_t *)(&ip6->ip6_dst))[i]) { rev = 1; break; } } } if (rev) { tha.src = ip6->ip6_dst; tha.dst = ip6->ip6_src; tha.port = dport << 16 | sport; } else { tha.dst = ip6->ip6_dst; tha.src = ip6->ip6_src; tha.port = sport << 16 | dport; } } else { if (sport > dport || (sport == dport && ip->ip_src.s_addr > ip->ip_dst.s_addr)) { rev = 1; } if (rev) { *(struct in_addr *)&tha.src = ip->ip_dst; *(struct in_addr *)&tha.dst = ip->ip_src; tha.port = dport << 16 | sport; } else { *(struct in_addr *)&tha.dst = ip->ip_dst; *(struct in_addr *)&tha.src = ip->ip_src; tha.port = sport << 16 | dport; } } #else if (sport < dport || (sport == dport && ip->ip_src.s_addr < ip->ip_dst.s_addr)) { tha.src = ip->ip_src, tha.dst = ip->ip_dst; tha.port = sport << 16 | dport; rev = 0; } else { tha.src = ip->ip_dst, tha.dst = ip->ip_src; tha.port = dport << 16 | sport; rev = 1; } #endif for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (!memcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr))) break; if (!th->nxt || flags & TH_SYN) { /* didn't find it or new conversation */ if (th->nxt == NULL) { th->nxt = calloc(1, sizeof(*th)); if (th->nxt == NULL) error("tcp_print: calloc"); } th->addr = tha; if (rev) th->ack = seq, th->seq = ack - 1; else th->seq = seq, th->ack = ack - 1; } else { if (rev) seq -= th->ack, ack -= th->seq; else seq -= th->seq, ack -= th->ack; } } hlen = tp->th_off * 4; if (hlen > length) { (void)printf(" [bad hdr length]"); return; } if (ip && ip->ip_v == 4 && vflag) { if (TTEST2(tp->th_sport, length)) { u_int16_t sum, tcp_sum; sum = tcp_cksum(ip, tp, length); if (sum != 0) { tcp_sum = EXTRACT_16BITS(&tp->th_sum); (void)printf(" [bad tcp cksum %x! -> %x]", tcp_sum, in_cksum_shouldbe(tcp_sum, sum)); } else (void)printf(" [tcp sum ok]"); } } #ifdef INET6 if (ip6 && ip6->ip6_plen && vflag) { if (TTEST2(tp->th_sport, length)) { u_int16_t sum, tcp_sum; sum = tcp6_cksum(ip6, tp, length); if (sum != 0) { tcp_sum = EXTRACT_16BITS(&tp->th_sum); (void)printf(" [bad tcp cksum %x! -> %x]", tcp_sum, in_cksum_shouldbe(tcp_sum, sum)); } else (void)printf(" [tcp sum ok]"); } } #endif /* OS Fingerprint */ if (oflag && (flags & (TH_SYN|TH_ACK)) == TH_SYN) { struct pf_osfp_enlist *head = NULL; struct pf_osfp_entry *fp; unsigned long left; left = (unsigned long)(snapend - (const u_char *)tp); if (left >= hlen) head = pf_osfp_fingerprint_hdr(ip, ip6, tp); if (head) { int prev = 0; printf(" (src OS:"); SLIST_FOREACH(fp, head, fp_entry) { if (fp->fp_enflags & PF_OSFP_EXPANDED) continue; if (prev) printf(","); printf(" %s", fp->fp_class_nm); if (fp->fp_version_nm[0]) printf(" %s", fp->fp_version_nm); if (fp->fp_subtype_nm[0]) printf(" %s", fp->fp_subtype_nm); prev = 1; } printf(")"); } else { if (left < hlen) printf(" (src OS: short-pkt)"); else printf(" (src OS: unknown)"); } }
/** * dccp_print - show dccp packet * @bp - beginning of dccp packet * @data2 - beginning of enclosing * @len - lenght of ip packet */ void dccp_print(const u_char *bp, const u_char *data2, u_int len) { const struct dccp_hdr *dh; const struct ip *ip; #ifdef INET6 const struct ip6_hdr *ip6; #endif const u_char *cp; u_short sport, dport; u_int hlen; u_int extlen = 0; dh = (const struct dccp_hdr *)bp; ip = (struct ip *)data2; #ifdef INET6 if (IP_V(ip) == 6) ip6 = (const struct ip6_hdr *)data2; else ip6 = NULL; #endif /*INET6*/ cp = (const u_char *)(dh + 1); if (cp > snapend) { printf("[Invalid packet|dccp]"); return; } if (len < sizeof(struct dccp_hdr)) { printf("truncated-dccp - %ld bytes missing!", (long)len - sizeof(struct dccp_hdr)); return; } sport = EXTRACT_16BITS(&dh->dccph_sport); dport = EXTRACT_16BITS(&dh->dccph_dport); hlen = dh->dccph_doff * 4; #ifdef INET6 if (ip6) { (void)printf("%s.%d > %s.%d: ", ip6addr_string(&ip6->ip6_src), sport, ip6addr_string(&ip6->ip6_dst), dport); } else #endif /*INET6*/ { (void)printf("%s.%d > %s.%d: ", ipaddr_string(&ip->ip_src), sport, ipaddr_string(&ip->ip_dst), dport); } fflush(stdout); if (qflag) { (void)printf(" %d", len - hlen); if (hlen > len) { (void)printf("dccp [bad hdr length %u - too long, > %u]", hlen, len); } return; } /* other variables in generic header */ if (vflag) { (void)printf("CCVal %d, CsCov %d, ", DCCPH_CCVAL(dh), DCCPH_CSCOV(dh)); } /* checksum calculation */ if (vflag && TTEST2(bp[0], len)) { u_int16_t sum = 0, dccp_sum; dccp_sum = EXTRACT_16BITS(&dh->dccph_checksum); (void)printf("cksum 0x%04x ", dccp_sum); if (IP_V(ip) == 4) sum = dccp_cksum(ip, dh, len); #ifdef INET6 else if (IP_V(ip) == 6) sum = dccp6_cksum(ip6, dh, len); #endif if (sum != 0) (void)printf("(incorrect -> 0x%04x), ",in_cksum_shouldbe(dccp_sum, sum)); else (void)printf("(correct), "); } switch (DCCPH_TYPE(dh)) { case DCCP_PKT_REQUEST: { struct dccp_hdr_request *dhr = (struct dccp_hdr_request *)(bp + dccp_basic_hdr_len(dh)); TCHECK(*dhr); (void)printf("request (service=%d) ", EXTRACT_32BITS(&dhr->dccph_req_service)); extlen += 4; break; } case DCCP_PKT_RESPONSE: { struct dccp_hdr_response *dhr = (struct dccp_hdr_response *)(bp + dccp_basic_hdr_len(dh)); TCHECK(*dhr); (void)printf("response (service=%d) ", EXTRACT_32BITS(&dhr->dccph_resp_service)); extlen += 12; break; } case DCCP_PKT_DATA: (void)printf("data "); break; case DCCP_PKT_ACK: { (void)printf("ack "); extlen += 8; break; } case DCCP_PKT_DATAACK: { (void)printf("dataack "); extlen += 8; break; } case DCCP_PKT_CLOSEREQ: (void)printf("closereq "); extlen += 8; break; case DCCP_PKT_CLOSE: (void)printf("close "); extlen += 8; break; case DCCP_PKT_RESET: { struct dccp_hdr_reset *dhr = (struct dccp_hdr_reset *)(bp + dccp_basic_hdr_len(dh)); TCHECK(*dhr); (void)printf("reset (code=%s) ", dccp_reset_code(dhr->dccph_reset_code)); extlen += 12; break; } case DCCP_PKT_SYNC: (void)printf("sync "); extlen += 8; break; case DCCP_PKT_SYNCACK: (void)printf("syncack "); extlen += 8; break; default: (void)printf("invalid "); break; } if ((DCCPH_TYPE(dh) != DCCP_PKT_DATA) && (DCCPH_TYPE(dh) != DCCP_PKT_REQUEST)) dccp_print_ack_no(bp); if (vflag < 2) return; (void)printf("seq %" PRIu64, dccp_seqno(dh)); /* process options */ if (hlen > dccp_basic_hdr_len(dh) + extlen){ const u_char *cp; u_int optlen; cp = bp + dccp_basic_hdr_len(dh) + extlen; printf(" <"); hlen -= dccp_basic_hdr_len(dh) + extlen; while(1){ TCHECK(*cp); optlen = dccp_print_option(cp); if (!optlen) goto trunc2; if (hlen <= optlen) break; hlen -= optlen; cp += optlen; printf(", "); } printf(">"); } return; trunc: printf("%s", tstr); trunc2: return; }
static void display_xip_serval(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *xip_serval_tree; proto_item *ti, *check_ti, *hl_ti; tvbuff_t *next_tvb; vec_t cksum_vec; gint offset; guint16 packet_checksum, actual_checksum; guint8 xsh_len, protocol, bytes_remaining; /* Get XIP Serval header length, stored as number of 32-bit words. */ xsh_len = tvb_get_guint8(tvb, XSRVL_LEN) << 2; /* Create XIP Serval header tree. */ ti = proto_tree_add_item(tree, proto_xip_serval, tvb, 0, xsh_len, ENC_NA); xip_serval_tree = proto_item_add_subtree(ti, ett_xip_serval_tree); /* Add XIP Serval header length. */ hl_ti = proto_tree_add_item(xip_serval_tree, hf_xip_serval_hl, tvb, XSRVL_LEN, 1, ENC_BIG_ENDIAN); proto_item_append_text(hl_ti, " bytes"); if (tvb_captured_length(tvb) < xsh_len) expert_add_info_format(pinfo, hl_ti, &ei_xip_serval_bad_len, "Header Length field (%d bytes) cannot be greater than actual number of bytes left in packet (%d bytes)", xsh_len, tvb_captured_length(tvb)); /* Add XIP Serval protocol. If it's not data, TCP, or UDP, the * packet is malformed. */ proto_tree_add_item(xip_serval_tree, hf_xip_serval_proto, tvb, XSRVL_PRO, 1, ENC_BIG_ENDIAN); protocol = tvb_get_guint8(tvb, XSRVL_PRO); if (!try_val_to_str(protocol, xip_serval_proto_vals)) expert_add_info_format(pinfo, ti, &ei_xip_serval_bad_proto, "Unrecognized protocol type: %d", protocol); /* Compute checksum. */ SET_CKSUM_VEC_TVB(cksum_vec, tvb, 0, xsh_len); actual_checksum = in_cksum(&cksum_vec, 1); /* Get XIP Serval checksum. */ packet_checksum = tvb_get_ntohs(tvb, XSRVL_CHK); if (actual_checksum == 0) { /* Add XIP Serval checksum as correct. */ proto_tree_add_uint_format(xip_serval_tree, hf_xip_serval_check, tvb, XSRVL_CHK, 2, packet_checksum, "Header checksum: 0x%04x [correct]", packet_checksum); } else { /* Add XIP Serval checksum as incorrect. */ check_ti = proto_tree_add_uint_format(xip_serval_tree, hf_xip_serval_check, tvb, XSRVL_CHK, 2, packet_checksum, "Header checksum: 0x%04x [incorrect, should be 0x%04x]", packet_checksum, in_cksum_shouldbe(packet_checksum, actual_checksum)); expert_add_info_format(pinfo, check_ti, &ei_xip_serval_bad_checksum, "Bad checksum"); } offset = XSRVL_EXT; /* If there's still more room, check for extension headers. */ bytes_remaining = xsh_len - offset; while (bytes_remaining >= XIP_SERVAL_EXT_MIN_LEN) { gint8 bytes_displayed = display_xip_serval_ext(tvb, pinfo, ti, xip_serval_tree, offset); /* Extension headers are malformed, so we can't say * what the rest of the packet holds. Stop dissecting. */ if (bytes_displayed <= 0) return; offset += bytes_displayed; bytes_remaining -= bytes_displayed; } switch (protocol) { case XIP_SERVAL_PROTO_DATA: next_tvb = tvb_new_subset_remaining(tvb, offset); call_dissector(data_handle, next_tvb, pinfo, tree); break; case IP_PROTO_TCP: { /* Get the Data Offset field of the TCP header, which is * the high nibble of the 12th octet and represents the * size of the TCP header of 32-bit words. */ guint8 tcp_len = hi_nibble(tvb_get_guint8(tvb, offset + 12))*4; next_tvb = tvb_new_subset(tvb, offset, tcp_len, tcp_len); call_dissector(tcp_handle, next_tvb, pinfo, tree); break; } case IP_PROTO_UDP: /* The UDP header is always 8 bytes. */ next_tvb = tvb_new_subset(tvb, offset, 8, 8); call_dissector(udp_handle, next_tvb, pinfo, tree); break; default: break; } }
/* main dissector */ static int dissect_xtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { guint32 offset, len; proto_item *ti; proto_tree *xtp_tree, *xtp_cmd_tree, *xtp_subtree; struct xtphdr xtph[1]; int error = 0; gchar *options = "<None>"; const char *fstr[] = { "<None>", "NOCHECK", "EDGE", "NOERR", "MULTI", "RES", "SORT", "NOFLOW", "FASTNAK", "SREQ", "DREQ", "RCLOSE", "WCLOSE", "EOM", "END", "BTAG" }; gint fpos = 0, returned_length; guint i, bpos; guint cmd_options; vec_t cksum_vec[1]; guint16 computed_cksum; gboolean have_btag; if ((len = tvb_length(tvb)) < XTP_HEADER_LEN) return 0; col_set_str(pinfo->cinfo, COL_PROTOCOL, "XTP"); col_clear(pinfo->cinfo, COL_INFO); /** parse header **/ offset = 0; /* key(8) */ xtph->key = tvb_get_ntohl(tvb, offset); xtph->key <<= 32; xtph->key += tvb_get_ntohl(tvb, offset+4); offset += 8; /* cmd(4) */ xtph->cmd = tvb_get_ntohl(tvb, offset); xtph->cmd_options = xtph->cmd >> 8; xtph->cmd_ptype = xtph->cmd & 0xff; xtph->cmd_ptype_ver = (xtph->cmd_ptype & 0xe0) >> 5; xtph->cmd_ptype_pformat = xtph->cmd_ptype & 0x1f; offset += 4; /* dlen(4) */ xtph->dlen = tvb_get_ntohl(tvb, offset); offset += 4; /* check(2) */ xtph->check = tvb_get_ntohs(tvb, offset); offset += 2; /* sort(2) */ xtph->sort = tvb_get_ntohs(tvb, offset); offset += 2; /* sync(4) */ xtph->sync = tvb_get_ntohl(tvb, offset); offset += 4; /* seq(8) */ xtph->seq = tvb_get_ntohl(tvb, offset); xtph->seq <<= 32; xtph->seq += tvb_get_ntohl(tvb, offset+4); #define MAX_OPTIONS_LEN 128 options=ep_alloc(MAX_OPTIONS_LEN); options[0]=0; cmd_options = xtph->cmd_options >> 8; for (i = 0; i < 16; i++) { bpos = 1 << (15 - i); if (cmd_options & bpos) { returned_length = g_snprintf(&options[fpos], MAX_OPTIONS_LEN-fpos, "%s%s", fpos?", ":"", fstr[i]); fpos += MIN(returned_length, MAX_OPTIONS_LEN-fpos); } } if (check_col(pinfo->cinfo, COL_INFO)) { col_add_str(pinfo->cinfo, COL_INFO, val_to_str(xtph->cmd_ptype_pformat, pformat_vals, "Unknown pformat (%u)")); col_append_fstr(pinfo->cinfo, COL_INFO, " [%s]", options); col_append_fstr(pinfo->cinfo, COL_INFO, " Seq=%" G_GINT64_MODIFIER "u", xtph->seq); col_append_fstr(pinfo->cinfo, COL_INFO, " Len=%u", xtph->dlen); } if (tree) { ti = proto_tree_add_item(tree, proto_xtp, tvb, 0, -1, FALSE); /** add summary **/ proto_item_append_text(ti, ", Key: 0x%016" G_GINT64_MODIFIER "X", xtph->key); proto_item_append_text(ti, ", Seq: %" G_GINT64_MODIFIER "u", xtph->seq); proto_item_append_text(ti, ", Len: %u", xtph->dlen); xtp_tree = proto_item_add_subtree(ti, ett_xtp); /* key(8) */ offset = 0; ti = proto_tree_add_uint64(xtp_tree, hf_xtp_key, tvb, offset, 8, xtph->key); xtp_subtree = proto_item_add_subtree(ti, ett_xtp_key); offset += 8; /* cmd(4) */ ti = proto_tree_add_uint(xtp_tree, hf_xtp_cmd, tvb, offset, 4, xtph->cmd); xtp_cmd_tree = proto_item_add_subtree(ti, ett_xtp_cmd); ti = proto_tree_add_uint(xtp_cmd_tree, hf_xtp_cmd_options, tvb, offset, 3, xtph->cmd_options); /** add summary **/ proto_item_append_text(ti, " [%s]", options); xtp_subtree = proto_item_add_subtree(ti, ett_xtp_cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_nocheck, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_edge, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_noerr, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_multi, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_res, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_sort, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_noflow, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_fastnak, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_sreq, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_dreq, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_rclose, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_wclose, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_eom, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_end, tvb, offset, 3, xtph->cmd_options); proto_tree_add_boolean(xtp_subtree, hf_xtp_cmd_options_btag, tvb, offset, 3, xtph->cmd_options); offset += 3; ti = proto_tree_add_uint(xtp_cmd_tree, hf_xtp_cmd_ptype, tvb, offset, 1, xtph->cmd_ptype); xtp_subtree = proto_item_add_subtree(ti, ett_xtp_cmd_ptype); proto_tree_add_uint(xtp_subtree, hf_xtp_cmd_ptype_ver, tvb, offset, 1, xtph->cmd_ptype_ver); if (xtph->cmd_ptype_ver != XTP_VERSION_4) { proto_item_append_text(ti, ", Unknown XTP version (%03X)", xtph->cmd_ptype_ver); error = 1; } proto_tree_add_uint(xtp_subtree, hf_xtp_cmd_ptype_pformat, tvb, offset, 1, xtph->cmd_ptype_pformat); offset++; /* dlen(4) */ ti = proto_tree_add_uint(xtp_tree, hf_xtp_dlen, tvb, offset, 4, xtph->dlen); if (xtph->dlen != len - XTP_HEADER_LEN) { proto_item_append_text(ti, ", bogus length (%u, must be %u)", xtph->dlen, len - XTP_HEADER_LEN); error = 1; } offset += 4; /* check(2) */ if (!pinfo->fragmented) { guint32 check_len = XTP_HEADER_LEN; if (!(xtph->cmd_options & XTP_CMD_OPTIONS_NOCHECK)) check_len += xtph->dlen; cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, check_len); cksum_vec[0].len = check_len; computed_cksum = in_cksum(cksum_vec, 1); if (computed_cksum == 0) { proto_tree_add_text(xtp_tree, tvb, offset, 2, "Checksum: 0x%04x [correct]", xtph->check); } else { proto_tree_add_text(xtp_tree, tvb, offset, 2, "Checksum: 0x%04x [incorrect, should be 0x%04x]", xtph->check, in_cksum_shouldbe(xtph->check, computed_cksum)); } } else { proto_tree_add_text(xtp_tree, tvb, offset, 2, "Checksum: 0x%04x", xtph->check); } offset += 2; /* sort(2) */ proto_tree_add_uint(xtp_tree, hf_xtp_sort, tvb, offset, 2, xtph->sort); offset += 2; /* sync(4) */ proto_tree_add_uint(xtp_tree, hf_xtp_sync, tvb, offset, 4, xtph->sync); offset += 4; /* seq(8) */ proto_tree_add_uint64(xtp_tree, hf_xtp_seq, tvb, offset, 8, xtph->seq); offset += 8; if (!error) { switch (xtph->cmd_ptype_pformat) { case XTP_DATA_PKT: have_btag = !!(xtph->cmd_options & XTP_CMD_OPTIONS_BTAG); dissect_xtp_data(tvb, xtp_tree, offset, have_btag); break; case XTP_CNTL_PKT: dissect_xtp_cntl(tvb, pinfo, xtp_tree, offset); break; case XTP_FIRST_PKT: dissect_xtp_first(tvb, xtp_tree, offset); break; case XTP_ECNTL_PKT: dissect_xtp_ecntl(tvb, pinfo, xtp_tree, offset); break; case XTP_TCNTL_PKT: dissect_xtp_tcntl(tvb, pinfo, xtp_tree, offset); break; case XTP_JOIN_PKT: /* obsolete */ break; case XTP_JCNTL_PKT: dissect_xtp_jcntl(tvb, pinfo, xtp_tree, offset); break; case XTP_DIAG_PKT: dissect_xtp_diag(tvb, xtp_tree, offset); break; default: /* error */ break; } } } return tvb_length(tvb); }
void tcp_print(register const u_char *bp, register u_int length, register const u_char *bp2, int fragmented) { register const struct tcphdr *tp; register const struct ip *ip; register u_char flags; register u_int hlen; register char ch; u_int16_t sport, dport, win, urp; u_int32_t seq, ack, thseq, thack; u_int utoval; int threv; #ifdef INET6 register const struct ip6_hdr *ip6; #endif tp = (struct tcphdr *)bp; ip = (struct ip *)bp2; #ifdef INET6 if (IP_V(ip) == 6) ip6 = (struct ip6_hdr *)bp2; else ip6 = NULL; #endif /*INET6*/ ch = '\0'; if (!TTEST(tp->th_dport)) { (void)printf("%s > %s: [|tcp]", ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst)); return; } sport = EXTRACT_16BITS(&tp->th_sport); dport = EXTRACT_16BITS(&tp->th_dport); hlen = TH_OFF(tp) * 4; /* * If data present, header length valid, and NFS port used, * assume NFS. * Pass offset of data plus 4 bytes for RPC TCP msg length * to NFS print routines. */ if (!qflag && hlen >= sizeof(*tp) && hlen <= length && (length - hlen) >= 4) { u_char *fraglenp; u_int32_t fraglen; register struct sunrpc_msg *rp; enum sunrpc_msg_type direction; fraglenp = (u_char *)tp + hlen; if (TTEST2(*fraglenp, 4)) { fraglen = EXTRACT_32BITS(fraglenp) & 0x7FFFFFFF; if (fraglen > (length - hlen) - 4) fraglen = (length - hlen) - 4; rp = (struct sunrpc_msg *)(fraglenp + 4); if (TTEST(rp->rm_direction)) { direction = (enum sunrpc_msg_type)EXTRACT_32BITS(&rp->rm_direction); if (dport == NFS_PORT && direction == SUNRPC_CALL) { nfsreq_print((u_char *)rp, fraglen, (u_char *)ip); return; } if (sport == NFS_PORT && direction == SUNRPC_REPLY) { nfsreply_print((u_char *)rp, fraglen, (u_char *)ip); return; } } } } #ifdef INET6 if (ip6) { if (ip6->ip6_nxt == IPPROTO_TCP) { (void)printf("%s.%s > %s.%s: ", ip6addr_string(&ip6->ip6_src), tcpport_string(sport), ip6addr_string(&ip6->ip6_dst), tcpport_string(dport)); } else { (void)printf("%s > %s: ", tcpport_string(sport), tcpport_string(dport)); } } else #endif /*INET6*/ { if (ip->ip_p == IPPROTO_TCP) { (void)printf("%s.%s > %s.%s: ", ipaddr_string(&ip->ip_src), tcpport_string(sport), ipaddr_string(&ip->ip_dst), tcpport_string(dport)); } else { (void)printf("%s > %s: ", tcpport_string(sport), tcpport_string(dport)); } } if (hlen < sizeof(*tp)) { (void)printf(" tcp %d [bad hdr length %u - too short, < %lu]", length - hlen, hlen, (unsigned long)sizeof(*tp)); return; } TCHECK(*tp); seq = EXTRACT_32BITS(&tp->th_seq); ack = EXTRACT_32BITS(&tp->th_ack); win = EXTRACT_16BITS(&tp->th_win); urp = EXTRACT_16BITS(&tp->th_urp); if (qflag) { (void)printf("tcp %d", length - hlen); if (hlen > length) { (void)printf(" [bad hdr length %u - too long, > %u]", hlen, length); } return; } flags = tp->th_flags; printf("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags)); if (!Sflag && (flags & TH_ACK)) { register struct tcp_seq_hash *th; const void *src, *dst; register int rev; struct tha tha; /* * Find (or record) the initial sequence numbers for * this conversation. (we pick an arbitrary * collating order so there's only one entry for * both directions). */ #ifdef INET6 rev = 0; if (ip6) { src = &ip6->ip6_src; dst = &ip6->ip6_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (memcmp(src, dst, sizeof ip6->ip6_dst) > 0) rev = 1; } if (rev) { memcpy(&tha.src, dst, sizeof ip6->ip6_dst); memcpy(&tha.dst, src, sizeof ip6->ip6_src); tha.port = dport << 16 | sport; } else { memcpy(&tha.dst, dst, sizeof ip6->ip6_dst); memcpy(&tha.src, src, sizeof ip6->ip6_src); tha.port = sport << 16 | dport; } } else { /* * Zero out the tha structure; the src and dst * fields are big enough to hold an IPv6 * address, but we only have IPv4 addresses * and thus must clear out the remaining 124 * bits. * * XXX - should we just clear those bytes after * copying the IPv4 addresses, rather than * zeroing out the entire structure and then * overwriting some of the zeroes? * * XXX - this could fail if we see TCP packets * with an IPv6 address with the lower 124 bits * all zero and also see TCP packes with an * IPv4 address with the same 32 bits as the * upper 32 bits of the IPv6 address in question. * Can that happen? Is it likely enough to be * an issue? */ memset(&tha, 0, sizeof(tha)); src = &ip->ip_src; dst = &ip->ip_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (memcmp(src, dst, sizeof ip->ip_dst) > 0) rev = 1; } if (rev) { memcpy(&tha.src, dst, sizeof ip->ip_dst); memcpy(&tha.dst, src, sizeof ip->ip_src); tha.port = dport << 16 | sport; } else { memcpy(&tha.dst, dst, sizeof ip->ip_dst); memcpy(&tha.src, src, sizeof ip->ip_src); tha.port = sport << 16 | dport; } } #else rev = 0; src = &ip->ip_src; dst = &ip->ip_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (memcmp(src, dst, sizeof ip->ip_dst) > 0) rev = 1; } if (rev) { memcpy(&tha.src, dst, sizeof ip->ip_dst); memcpy(&tha.dst, src, sizeof ip->ip_src); tha.port = dport << 16 | sport; } else { memcpy(&tha.dst, dst, sizeof ip->ip_dst); memcpy(&tha.src, src, sizeof ip->ip_src); tha.port = sport << 16 | dport; } #endif threv = rev; for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (memcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr)) == 0) break; if (!th->nxt || (flags & TH_SYN)) { /* didn't find it or new conversation */ if (th->nxt == NULL) { th->nxt = (struct tcp_seq_hash *) calloc(1, sizeof(*th)); if (th->nxt == NULL) error("tcp_print: calloc"); } th->addr = tha; if (rev) th->ack = seq, th->seq = ack - 1; else th->seq = seq, th->ack = ack - 1; } else { if (rev) seq -= th->ack, ack -= th->seq; else seq -= th->seq, ack -= th->ack; } thseq = th->seq; thack = th->ack; } else { /*fool gcc*/ thseq = thack = threv = 0; } if (hlen > length) { (void)printf(" [bad hdr length %u - too long, > %u]", hlen, length); return; } if (vflag && !Kflag && !fragmented) { /* Check the checksum, if possible. */ u_int16_t sum, tcp_sum; if (IP_V(ip) == 4) { if (TTEST2(tp->th_sport, length)) { sum = tcp_cksum(ip, tp, length); tcp_sum = EXTRACT_16BITS(&tp->th_sum); (void)printf(", cksum 0x%04x", tcp_sum); if (sum != 0) (void)printf(" (incorrect -> 0x%04x)", in_cksum_shouldbe(tcp_sum, sum)); else (void)printf(" (correct)"); } } #ifdef INET6 else if (IP_V(ip) == 6 && ip6->ip6_plen) { if (TTEST2(tp->th_sport, length)) { sum = nextproto6_cksum(ip6, (const u_int8_t *)tp, length, IPPROTO_TCP); tcp_sum = EXTRACT_16BITS(&tp->th_sum); (void)printf(", cksum 0x%04x", tcp_sum); if (sum != 0) (void)printf(" (incorrect -> 0x%04x)", in_cksum_shouldbe(tcp_sum, sum)); else (void)printf(" (correct)"); } } #endif } length -= hlen; if (vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) { (void)printf(", seq %u", seq); if (length > 0) { (void)printf(":%u", seq + length); } } if (flags & TH_ACK) { (void)printf(", ack %u", ack); } (void)printf(", win %d", win); if (flags & TH_URG) (void)printf(", urg %d", urp); /* * Handle any options. */ if (hlen > sizeof(*tp)) { register const u_char *cp; register u_int i, opt, datalen; register u_int len; hlen -= sizeof(*tp); cp = (const u_char *)tp + sizeof(*tp); printf(", options ["); while (hlen > 0) { if (ch != '\0') putchar(ch); TCHECK(*cp); opt = *cp++; if (ZEROLENOPT(opt)) len = 1; else { TCHECK(*cp); len = *cp++; /* total including type, len */ if (len < 2 || len > hlen) goto bad; --hlen; /* account for length byte */ } --hlen; /* account for type byte */ datalen = 0; /* Bail if "l" bytes of data are not left or were not captured */ #define LENCHECK(l) { if ((l) > hlen) goto bad; TCHECK2(*cp, l); } printf("%s", tok2str(tcp_option_values, "Unknown Option %u", opt)); switch (opt) { case TCPOPT_MAXSEG: datalen = 2; LENCHECK(datalen); (void)printf(" %u", EXTRACT_16BITS(cp)); break; case TCPOPT_WSCALE: datalen = 1; LENCHECK(datalen); (void)printf(" %u", *cp); break; case TCPOPT_SACK: datalen = len - 2; if (datalen % 8 != 0) { (void)printf("malformed sack"); } else { u_int32_t s, e; (void)printf(" %d ", datalen / 8); for (i = 0; i < datalen; i += 8) { LENCHECK(i + 4); s = EXTRACT_32BITS(cp + i); LENCHECK(i + 8); e = EXTRACT_32BITS(cp + i + 4); if (threv) { s -= thseq; e -= thseq; } else { s -= thack; e -= thack; } (void)printf("{%u:%u}", s, e); } } break; case TCPOPT_CC: case TCPOPT_CCNEW: case TCPOPT_CCECHO: case TCPOPT_ECHO: case TCPOPT_ECHOREPLY: /* * those options share their semantics. * fall through */ datalen = 4; LENCHECK(datalen); (void)printf(" %u", EXTRACT_32BITS(cp)); break; case TCPOPT_TIMESTAMP: datalen = 8; LENCHECK(datalen); (void)printf(" val %u ecr %u", EXTRACT_32BITS(cp), EXTRACT_32BITS(cp + 4)); break; case TCPOPT_SIGNATURE: datalen = TCP_SIGLEN; LENCHECK(datalen); #ifdef HAVE_LIBCRYPTO switch (tcp_verify_signature(ip, tp, bp + TH_OFF(tp) * 4, length, cp)) { case SIGNATURE_VALID: (void)printf("valid"); break; case SIGNATURE_INVALID: (void)printf("invalid"); break; case CANT_CHECK_SIGNATURE: (void)printf("can't check - "); for (i = 0; i < TCP_SIGLEN; ++i) (void)printf("%02x", cp[i]); break; } #else for (i = 0; i < TCP_SIGLEN; ++i) (void)printf("%02x", cp[i]); #endif break; case TCPOPT_AUTH: (void)printf("keyid %d", *cp++); datalen = len - 3; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } break; case TCPOPT_EOL: case TCPOPT_NOP: case TCPOPT_SACKOK: /* * Nothing interesting. * fall through */ break; case TCPOPT_UTO: datalen = 2; LENCHECK(datalen); utoval = EXTRACT_16BITS(cp); (void)printf("0x%x", utoval); if (utoval & 0x0001) utoval = (utoval >> 1) * 60; else utoval >>= 1; (void)printf(" %u", utoval); break; case TCPOPT_MPTCP: { uint8_t subtype; datalen = 1; LENCHECK(datalen); subtype = (*cp) >> 4; printf(" %s ", tok2str(mptcp_subtypes, "Unknown MPTCP subtype %u", subtype)); switch (subtype) { case TCPOPT_MPTCP_MP_CAPABLE: { uint8_t version = (*cp) & 0x0f; uint8_t mpflags; if (version != 0) { printf(" version %u ", version); for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } break; } datalen += 1; LENCHECK(datalen); mpflags = cp[1]; printf("%s%s%s%s%s%s%s%s%s", (mpflags) ? "flags:" : "", (mpflags & 0x80) ? "A" : "", (mpflags & 0x40) ? "B" : "", (mpflags & 0x20) ? "C" : "", (mpflags & 0x10) ? "D" : "", (mpflags & 0x08) ? "E" : "", (mpflags & 0x04) ? "F" : "", (mpflags & 0x02) ? "G" : "", (mpflags & 0x01) ? "H" : ""); if (len == 12 || len == 20) { printf(" sndkey:"); for (i = 0; i < 8; ++i) { datalen++; LENCHECK(datalen); (void)printf("%02x", cp[2 + i]); } if (len == 20) { printf(" rcvkey:"); for (i = 0; i < 8; ++i) { datalen++; LENCHECK(datalen); (void)printf("%02x", cp[10 + i]); } } } else { printf(" unknown:"); datalen = len - 2; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } } break; } case TCPOPT_MPTCP_MP_JOIN: { uint8_t mpflags = (*cp) & 0x0f; /* Flags on SYN only */ if (flags & TH_SYN) { printf("%s%s%s%s%s", (mpflags) ? "flags:" : "", (mpflags & 0x08) ? "0" : "", (mpflags & 0x04) ? "1" : "", (mpflags & 0x02) ? "2" : "", (mpflags & 0x01) ? "B" : ""); } /* Address ID on SYN only, otherwise ignored */ datalen += 1; LENCHECK(datalen); if ((flags & TH_SYN)) printf(" addrid:%0x", cp[1]); if (flags == TH_SYN && len == 12) { /* Initial SYN */ printf(" rcvtok:"); for (i = 0; i < 4; ++i) { datalen++; LENCHECK(datalen); (void)printf("%02x", cp[2 + i]); } printf(" sndrand:"); for (i = 0; i < 4; ++i) { datalen++; LENCHECK(datalen); (void)printf("%02x", cp[6 + i]); } } else if ((flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK) && len == 16) { /* Responding SYN/ACK */ printf(" sndhmac:"); for (i = 0; i < 8; ++i) { datalen++; LENCHECK(datalen); (void)printf("%02x", cp[2 + i]); } printf(" sndrand:"); for (i = 0; i < 4; ++i) { datalen++; LENCHECK(datalen); (void)printf("%02x", cp[8 + i]); } } else if ((flags & (TH_SYN | TH_ACK)) == TH_ACK && len == 24) { /* Third ACK */ printf(" sndhmac:"); for (i = 0; i < 20; ++i) { datalen++; LENCHECK(i); (void)printf("%02x", cp[2 + i]); } } else { datalen = len - 2; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } } break; } case TCPOPT_MPTCP_DSS: { uint8_t mpflags; u_int ack_len = 0; u_int dsn_len = 0; u_int64_t dack; u_int64_t dsn; u_int32_t sfsn; u_int16_t dlen; u_int16_t csum; datalen += 1; LENCHECK(datalen); mpflags = cp[1] & 0x1f; printf("%s%s%s%s%s%s", (mpflags) ? "flags:" : "", (mpflags & 0x10) ? "F" : "", (mpflags & 0x08) ? "m" : "", (mpflags & 0x04) ? "M" : "", (mpflags & 0x02) ? "a" : "", (mpflags & 0x01) ? "A" : ""); if ((mpflags & MPDSS_FLAG_A)) { if ((mpflags & MPDSS_FLAG_a)) { ack_len = 8; datalen += ack_len; LENCHECK(datalen); dack = EXTRACT_64BITS(cp + 2); } else { ack_len = 4; datalen += ack_len; LENCHECK(datalen); dack = EXTRACT_32BITS(cp + 2); } (void)printf(" dack: %" PRIu64, dack); } if ((mpflags & MPDSS_FLAG_M)) { if ((mpflags & MPDSS_FLAG_m)) { dsn_len = 8; datalen += dsn_len; LENCHECK(datalen); dsn = EXTRACT_64BITS(cp + 2 + ack_len); } else { dsn_len = 4; datalen += dsn_len; LENCHECK(datalen); dsn = EXTRACT_32BITS(cp + 2 + ack_len); } (void)printf(" dsn: %" PRIu64, dsn); datalen += 4; LENCHECK(datalen); sfsn = EXTRACT_32BITS(cp + 2 + ack_len + dsn_len); (void)printf(" sfsn: %" PRIu32, sfsn); datalen += 2; LENCHECK(datalen); dlen = EXTRACT_16BITS(cp + 2 + ack_len + dsn_len + 4); (void)printf(" dlen: %" PRIu16, dlen); /* * Use the length of the option to find out if * the checksum is present */ if (datalen < len - 2) { datalen += 2; LENCHECK(datalen); csum = EXTRACT_16BITS(cp + 2 + ack_len + dsn_len + 6); (void)printf(" csum: %" PRIu16, csum); } } break; } case TCPOPT_MPTCP_ADD_ADDR: { uint8_t ipvers; u_int addrlen = 0; u_int16_t port; ipvers = cp[1] & 0xf0; printf(" vers:%u", ipvers); datalen = 2; LENCHECK(datalen); printf(" addrid:%0x", cp[1]); switch (ipvers) { case 4: { datalen = 6; LENCHECK(datalen); ipaddr_string(cp + 2); break; } case 6: { datalen = 18; LENCHECK(datalen); #ifdef INET6 ip6addr_string(cp + 2); #endif break; } default: goto bad; } /* * Use the length of the option to find out if * the port is present */ if (datalen < len - 2) { datalen += 2; LENCHECK(datalen); port = EXTRACT_16BITS(cp + 2 + addrlen); printf(" port: %u", port); } break; } case TCPOPT_MPTCP_REMOVE_ADDR: datalen = len - 2; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf(" %u", cp[i]); } break; case TCPOPT_MPTCP_MP_PRIO: { uint8_t mpflags = (*cp) & 0x0f; if (mpflags == 0x01) printf("flag B"); else if (mpflags != 0) { printf("flag %c%c%c%c%c%c%c%c", mpflags & 0x80 ? '1' : '0', mpflags & 0x40 ? '1' : '0', mpflags & 0x20 ? '1' : '0', mpflags & 0x10 ? '1' : '0', mpflags & 0x08 ? '1' : '0', mpflags & 0x04 ? '1' : '0', mpflags & 0x02 ? '1' : '0', mpflags & 0x01 ? 'B' : '0'); } if (len == 4) { datalen = 2; LENCHECK(datalen); printf("%saddrid:%u", mpflags ? " " : "", cp[1]); } break; } case TCPOPT_MPTCP_MP_FAIL: datalen = 10; LENCHECK(datalen); printf(" dsn:"); for (i = 0; i < 8; ++i) { (void)printf("%02x", cp[2 + i]); } break; case TCPOPT_MPTCP_MP_FASTCLOSE: datalen = 10; LENCHECK(datalen); printf(" rcvrkey:"); for (i = 0; i < 8; ++i) { (void)printf("%02x", cp[2 + i]); } break; default: datalen = len - 2; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } break; } break; } default: datalen = len - 2; for (i = 0; i < datalen; ++i) { LENCHECK(i); (void)printf("%02x", cp[i]); } break; } /* Account for data printed */ cp += datalen; hlen -= datalen; /* Check specification against observed length */ ++datalen; /* option octet */ if (!ZEROLENOPT(opt)) ++datalen; /* size octet */ if (datalen != len) (void)printf("[len %d]", len); ch = ','; if (opt == TCPOPT_EOL) break; } putchar(']'); }
static void dissect_stt_checksum(tvbuff_t *tvb, packet_info *pinfo, proto_tree *stt_tree) { proto_tree *checksum_tree; proto_item *item; guint16 checksum = tvb_get_ntohs(tvb, 16); gboolean can_checksum; guint16 computed_cksum; gboolean checksum_good = FALSE, checksum_bad = FALSE; item = proto_tree_add_uint_format_value(stt_tree, hf_stt_checksum, tvb, 16, 2, checksum, "0x%04x", checksum); can_checksum = !pinfo->fragmented && tvb_bytes_exist(tvb, 0, tvb_reported_length(tvb)); if (can_checksum && pref_check_checksum) { vec_t cksum_vec[4]; guint32 phdr[2]; /* Set up the fields of the pseudo-header. */ SET_CKSUM_VEC_PTR(cksum_vec[0], (const guint8 *)pinfo->src.data, pinfo->src.len); SET_CKSUM_VEC_PTR(cksum_vec[1], (const guint8 *)pinfo->dst.data, pinfo->dst.len); switch (pinfo->src.type) { case AT_IPv4: phdr[0] = g_htonl((IP_PROTO_TCP<<16) + tvb_reported_length(tvb)); SET_CKSUM_VEC_PTR(cksum_vec[2], (const guint8 *)phdr, 4); break; case AT_IPv6: phdr[0] = g_htonl(tvb_reported_length(tvb)); phdr[1] = g_htonl(IP_PROTO_TCP); SET_CKSUM_VEC_PTR(cksum_vec[2], (const guint8 *)phdr, 8); break; default: /* STT runs only atop IPv4 and IPv6.... */ DISSECTOR_ASSERT_NOT_REACHED(); break; } SET_CKSUM_VEC_TVB(cksum_vec[3], tvb, 0, tvb_reported_length(tvb)); computed_cksum = in_cksum(cksum_vec, 4); checksum_good = (computed_cksum == 0); checksum_bad = !checksum_good; if (checksum_good) { proto_item_append_text(item, " [correct]"); } else if (checksum_bad) { guint16 expected_cksum = in_cksum_shouldbe(checksum, computed_cksum); proto_item_append_text(item, " [incorrect, should be 0x%04x (maybe caused by \"TCP checksum offload\"?)]", expected_cksum); expert_add_info(pinfo, item, &ei_stt_checksum_bad); checksum = expected_cksum; } } else if (pref_check_checksum) { proto_item_append_text(item, " [unchecked, not all data available]"); } else { proto_item_append_text(item, " [validation disabled]"); } checksum_tree = proto_item_add_subtree(item, ett_stt_checksum); if (checksum_good || checksum_bad) { item = proto_tree_add_uint(checksum_tree, hf_stt_checksum_calculated, tvb, 16, 2, checksum); PROTO_ITEM_SET_GENERATED(item); } item = proto_tree_add_boolean(checksum_tree, hf_stt_checksum_good, tvb, 16, 2, checksum_good); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_boolean(checksum_tree, hf_stt_checksum_bad, tvb, 16, 2, checksum_bad); PROTO_ITEM_SET_GENERATED(item); }