Fix bug 6561: IPv4 UDP/TCP Checksum incorrect if routing header present.

https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6561

svn path=/trunk/; revision=39961
This commit is contained in:
Chris Maynard 2011-11-20 15:47:14 +00:00
parent 1e6a19e48a
commit 3c8e31e055
1 changed files with 124 additions and 26 deletions

View File

@ -1233,6 +1233,90 @@ dissect_ip_tcp_options(tvbuff_t *tvb, int offset, guint length,
}
}
/* This function searches the IP options for either a loose or strict source
* route option, then returns the offset to the destination address if the
* pointer is still valid or zero if the pointer is greater than the length.
*
* The guts of this function was taken from dissect_ip_tcp_options().
*/
static int
get_dst_offset(tvbuff_t *tvb, int offset, guint length,
const ip_tcp_opt *opttab, int nopts, int eol)
{
guchar opt;
const ip_tcp_opt *optp;
opt_len_type len_type;
unsigned int optlen;
guint len;
int orig_offset = offset;
while (length > 0) {
opt = tvb_get_guint8(tvb, offset);
for (optp = &opttab[0]; optp < &opttab[nopts]; optp++) {
if (optp->optcode == opt)
break;
}
if (optp == &opttab[nopts]) {
/* We assume that the only NO_LENGTH options are EOL and NOP options,
so that we can treat unknown options as VARIABLE_LENGTH with a
minimum of 2, and at least be able to move on to the next option
by using the length in the option. */
optp = NULL; /* indicate that we don't know this option */
len_type = VARIABLE_LENGTH;
optlen = 2;
} else {
len_type = optp->len_type;
optlen = optp->optlen;
}
--length; /* account for type byte */
if (len_type != NO_LENGTH) {
/* Option has a length. Is it in the packet? */
if (length == 0) {
/* Bogus - packet must at least include option code byte and
length byte! */
return 0;
}
len = tvb_get_guint8(tvb, offset + 1); /* total including type, len */
--length; /* account for length byte */
if (len < 2) {
/* Bogus - option length is too short to include option code and
option length. */
return 0;
} else if (len - 2 > length) {
/* Bogus - option goes past the end of the header. */
return 0;
} else if (len_type == FIXED_LENGTH && len != optlen) {
/* Bogus - option length isn't what it's supposed to be for this
option. */
return 0;
} else if (len_type == VARIABLE_LENGTH && len < optlen) {
/* Bogus - option length is less than what it's supposed to be for
this option. */
return 0;
} else if (optp != NULL) {
if (opt == IPOPT_SSRR || opt == IPOPT_LSRR) {
/* Hmm, what if you have both options? */
guint8 ptr;
ptr = tvb_get_guint8(tvb, offset + 2);
if (ptr < 4 || (ptr & 3) || (ptr > len)) {
return 0;
}
return (offset - orig_offset) + 4 + (len - 4);
}
len -= 2; /* subtract size of type and length */
offset += 2 + len;
}
length -= len;
} else {
offset += 1;
}
if (opt == eol)
return 0;
}
return 0;
}
/* Returns the valid ttl for the group address */
static guint16
local_network_control_block_addr_valid_ttl(guint32 addr)
@ -1338,7 +1422,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
proto_tree *ip_tree = NULL, *field_tree= NULL;
proto_item *ti = NULL, *tf;
guint32 addr;
int offset = 0;
int offset = 0, dst_off;
guint hlen, optlen;
guint16 flags;
guint8 nxt;
@ -1585,15 +1669,30 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
PROTO_ITEM_SET_GENERATED(item);
PROTO_ITEM_SET_HIDDEN(item);
}
dst_addr = tvb_get_ptr(tvb, offset + IPH_DST, 4);
dst32 = tvb_get_ntohl(tvb, offset + IPH_DST);
/* If there's an IP strict or loose source routing option, then the final
* L3 IP destination address will be the last entry in the routing header
* EXCEPT when the table is exhausted (pointer is greater than the length).
* In this case, the final L3 IP destination address is the one in the L3
* header. (REF: http://tools.ietf.org/html/rfc791#section-3.1)
*/
if (hlen > IPH_MIN_LEN) {
/* There's more than just the fixed-length header. See if we've got
* either a strict or loose source route option and if so, return the
* offset into the tvb to where the real destination IP address is located.
*/
dst_off = get_dst_offset(tvb, offset + 20, hlen - IPH_MIN_LEN, ipopts,
N_IP_OPTS, IPOPT_END);
}
else
dst_off = 0;
dst_addr = tvb_get_ptr(tvb, offset + IPH_DST + dst_off, 4);
dst32 = tvb_get_ntohl(tvb, offset + IPH_DST + dst_off);
SET_ADDRESS(&pinfo->net_dst, AT_IPv4, 4, dst_addr);
SET_ADDRESS(&pinfo->dst, AT_IPv4, 4, dst_addr);
SET_ADDRESS(&iph->ip_dst, AT_IPv4, 4, dst_addr);
tap_queue_packet(ip_tap, pinfo, iph);
/* If an IP is destined for an IP address in the Local Network Control Block
* (e.g. 224.0.0.0/24), the packet should never be routed and the TTL would
* be expected to be 1. (see RFC 3171) Flag a TTL greater than 1.
@ -1620,13 +1719,13 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
if (ip_summary_in_tree) {
proto_item_append_text(ti, ", Dst: %s (%s)", dst_host, ip_to_str(iph->ip_dst.data));
}
proto_tree_add_ipv4(ip_tree, hf_ip_dst, tvb, offset + 16, 4, addr);
item = proto_tree_add_ipv4(ip_tree, hf_ip_addr, tvb, offset + 16, 4, addr);
proto_tree_add_ipv4(ip_tree, hf_ip_dst, tvb, offset + 16 + dst_off, 4, addr);
item = proto_tree_add_ipv4(ip_tree, hf_ip_addr, tvb, offset + 16 + dst_off, 4, addr);
PROTO_ITEM_SET_HIDDEN(item);
item = proto_tree_add_string(ip_tree, hf_ip_dst_host, tvb, offset + 16, 4, dst_host);
item = proto_tree_add_string(ip_tree, hf_ip_dst_host, tvb, offset + 16 + dst_off, 4, dst_host);
PROTO_ITEM_SET_GENERATED(item);
PROTO_ITEM_SET_HIDDEN(item);
item = proto_tree_add_string(ip_tree, hf_ip_host, tvb, offset + 16, 4, dst_host);
item = proto_tree_add_string(ip_tree, hf_ip_host, tvb, offset + 16 + dst_off, 4, dst_host);
PROTO_ITEM_SET_GENERATED(item);
PROTO_ITEM_SET_HIDDEN(item);
}
@ -1652,10 +1751,9 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
}
pinfo->ipproto = iph->ip_p;
pinfo->iplen = iph->ip_len;
pinfo->iphdrlen = hlen;
tap_queue_packet(ip_tap, pinfo, iph);
/* Skip over header + options */
offset += hlen;
@ -1742,7 +1840,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
} else if (!dissector_try_uint(ip_dissector_table, nxt, next_tvb, pinfo, parent_tree)) {
/* Unknown protocol */
if (update_col_info) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s (0x%02x)", ipprotostr(iph->ip_p), iph->ip_p);
col_add_fstr(pinfo->cinfo, COL_INFO, "%s (%u)", ipprotostr(iph->ip_p), iph->ip_p);
}
call_dissector(data_handle,next_tvb, pinfo, parent_tree);
}