2013-06-14 14:35:34 +00:00
|
|
|
/* packet-carp.c
|
|
|
|
* Routines for the Common Address Redundancy Protocol (CARP)
|
|
|
|
* Copyright 2013, Uli Heilmeier <uh@heilmeier.eu>
|
|
|
|
* Based on packet-vrrp.c by Heikki Vatiainen <hessu@cs.tut.fi>
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <epan/packet.h>
|
|
|
|
#include <epan/ipproto.h>
|
|
|
|
#include <epan/in_cksum.h>
|
|
|
|
|
2013-12-15 23:44:12 +00:00
|
|
|
void proto_register_carp(void);
|
|
|
|
void proto_reg_handoff_carp(void);
|
|
|
|
|
2013-06-14 14:35:34 +00:00
|
|
|
static gint proto_carp = -1;
|
|
|
|
static gint ett_carp = -1;
|
|
|
|
static gint ett_carp_ver_type = -1;
|
|
|
|
|
|
|
|
static gint hf_carp_ver_type = -1;
|
|
|
|
static gint hf_carp_version = -1;
|
|
|
|
static gint hf_carp_type = -1;
|
|
|
|
static gint hf_carp_vhid = -1;
|
|
|
|
static gint hf_carp_advskew = -1;
|
|
|
|
static gint hf_carp_authlen = -1;
|
|
|
|
static gint hf_carp_demotion = -1;
|
|
|
|
static gint hf_carp_advbase = -1;
|
|
|
|
static gint hf_carp_counter = -1;
|
|
|
|
static gint hf_carp_hmac = -1;
|
2014-08-01 14:08:57 +00:00
|
|
|
static gint hf_carp_checksum = -1;
|
2013-06-14 14:35:34 +00:00
|
|
|
|
|
|
|
#define CARP_VERSION_MASK 0xf0
|
|
|
|
#define CARP_TYPE_MASK 0x0f
|
|
|
|
|
|
|
|
#define CARP_TYPE_ADVERTISEMENT 1
|
|
|
|
static const value_string carp_type_vals[] = {
|
|
|
|
{CARP_TYPE_ADVERTISEMENT, "Advertisement"},
|
|
|
|
{0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static gboolean
|
2013-06-14 14:56:00 +00:00
|
|
|
test_carp_packet(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_)
|
2013-06-14 14:35:34 +00:00
|
|
|
{
|
|
|
|
guint8 ver_type, version, auth_length;
|
|
|
|
|
2013-10-13 19:56:52 +00:00
|
|
|
/* First some simple check if the data is
|
2013-06-14 14:35:34 +00:00
|
|
|
really CARP */
|
2015-04-16 11:31:57 +00:00
|
|
|
if (tvb_captured_length(tvb) < 36)
|
2013-06-14 14:35:34 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* Version must be 1 or 2, type must be in carp_type_vals */
|
|
|
|
ver_type = tvb_get_guint8(tvb, 0);
|
|
|
|
version = hi_nibble(ver_type);
|
|
|
|
if ((version == 0) || (version > 2) ||
|
|
|
|
(try_val_to_str(lo_nibble(ver_type), carp_type_vals) == NULL))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
auth_length = tvb_get_guint8(tvb, 3);
|
|
|
|
if ( auth_length != 7 )
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2013-12-10 03:11:34 +00:00
|
|
|
dissect_carp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
|
2013-06-14 14:35:34 +00:00
|
|
|
{
|
|
|
|
int offset = 0;
|
Clean up Internet checksum handling.
Add macros to set entries of a vec_t, one for use when you have a
pointer to private data, and one for use when you have data in a tvbuff.
The latter wraps the use of tvb_get_ptr(), so that you're not directly
calling it in a dissector.
Move ip_checksum() to epan/in_cksum.c, and add an ip_checksum_tvb() that
wraps the use of tvb_get_ptr().
In the CARP dissector, give the length variable an unsigned type -
there's no benefit to it being signed, and that requires some casts to
be thrown around.
In the DCCP dissector, check only against the coverage length to see if
we have enough data, combine the "should we check the checksum?" check
with the "*can* we check the checksum?" check in a single if, and throw
a dissector assertion if the source network address type isn't IPv4 or
IPv6.
Get rid of inclues of <epan/in_cksum.h> in dissectors that don't use any
of the Internet checksum routines.
In the HIP dissector, make sure we have the data to calculate the
checksum before doing so.
Change-Id: I2f9674775dbb54c533d33082632809f7d32ec8ae
Reviewed-on: https://code.wireshark.org/review/3517
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2014-08-09 01:09:00 +00:00
|
|
|
guint carp_len;
|
2013-06-14 14:35:34 +00:00
|
|
|
guint8 vhid;
|
|
|
|
vec_t cksum_vec[4];
|
|
|
|
proto_item *ti, *tv;
|
|
|
|
proto_tree *carp_tree, *ver_type_tree;
|
|
|
|
guint8 ver_type;
|
|
|
|
guint16 cksum, computed_cksum;
|
|
|
|
|
|
|
|
/* Make sure it's a CARP packet */
|
|
|
|
if (!test_carp_packet(tvb, pinfo, tree, data))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "CARP");
|
|
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
|
|
|
|
vhid = tvb_get_guint8(tvb, 1);
|
|
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "%s (Virtual Host ID: %u)",
|
|
|
|
"Announcement", vhid);
|
|
|
|
|
|
|
|
ti = proto_tree_add_item(tree, proto_carp, tvb, 0, -1, ENC_NA);
|
|
|
|
carp_tree = proto_item_add_subtree(ti, ett_carp);
|
|
|
|
|
|
|
|
ver_type = tvb_get_guint8(tvb, 0);
|
|
|
|
tv = proto_tree_add_uint_format(carp_tree, hf_carp_ver_type,
|
|
|
|
tvb, offset, 1, ver_type,
|
|
|
|
"Version %u, Packet type %u (%s)",
|
|
|
|
hi_nibble(ver_type), lo_nibble(ver_type),
|
|
|
|
val_to_str_const(lo_nibble(ver_type), carp_type_vals, "Unknown"));
|
|
|
|
ver_type_tree = proto_item_add_subtree(tv, ett_carp_ver_type);
|
|
|
|
proto_tree_add_uint(ver_type_tree, hf_carp_version, tvb, offset, 1, ver_type);
|
|
|
|
proto_tree_add_uint(ver_type_tree, hf_carp_type, tvb, offset, 1, ver_type);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
proto_tree_add_item(carp_tree, hf_carp_vhid, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
proto_tree_add_item(carp_tree, hf_carp_advskew, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
proto_tree_add_item(carp_tree, hf_carp_authlen, tvb,
|
2014-12-13 17:52:20 +00:00
|
|
|
offset, 1, ENC_BIG_ENDIAN);
|
2013-06-14 14:35:34 +00:00
|
|
|
offset++;
|
|
|
|
|
|
|
|
proto_tree_add_item(carp_tree, hf_carp_demotion, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
proto_tree_add_item(carp_tree, hf_carp_advbase, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
cksum = tvb_get_ntohs(tvb, offset);
|
2014-08-01 14:08:57 +00:00
|
|
|
ti = proto_tree_add_item(carp_tree, hf_carp_checksum, tvb, offset, 2, ENC_BIG_ENDIAN);
|
Clean up Internet checksum handling.
Add macros to set entries of a vec_t, one for use when you have a
pointer to private data, and one for use when you have data in a tvbuff.
The latter wraps the use of tvb_get_ptr(), so that you're not directly
calling it in a dissector.
Move ip_checksum() to epan/in_cksum.c, and add an ip_checksum_tvb() that
wraps the use of tvb_get_ptr().
In the CARP dissector, give the length variable an unsigned type -
there's no benefit to it being signed, and that requires some casts to
be thrown around.
In the DCCP dissector, check only against the coverage length to see if
we have enough data, combine the "should we check the checksum?" check
with the "*can* we check the checksum?" check in a single if, and throw
a dissector assertion if the source network address type isn't IPv4 or
IPv6.
Get rid of inclues of <epan/in_cksum.h> in dissectors that don't use any
of the Internet checksum routines.
In the HIP dissector, make sure we have the data to calculate the
checksum before doing so.
Change-Id: I2f9674775dbb54c533d33082632809f7d32ec8ae
Reviewed-on: https://code.wireshark.org/review/3517
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2014-08-09 01:09:00 +00:00
|
|
|
carp_len = tvb_reported_length(tvb);
|
2015-04-16 11:31:57 +00:00
|
|
|
if (!pinfo->fragmented && tvb_captured_length(tvb) >= carp_len) {
|
2013-06-14 14:35:34 +00:00
|
|
|
/* The packet isn't part of a fragmented datagram
|
|
|
|
and isn't truncated, so we can checksum it. */
|
Clean up Internet checksum handling.
Add macros to set entries of a vec_t, one for use when you have a
pointer to private data, and one for use when you have data in a tvbuff.
The latter wraps the use of tvb_get_ptr(), so that you're not directly
calling it in a dissector.
Move ip_checksum() to epan/in_cksum.c, and add an ip_checksum_tvb() that
wraps the use of tvb_get_ptr().
In the CARP dissector, give the length variable an unsigned type -
there's no benefit to it being signed, and that requires some casts to
be thrown around.
In the DCCP dissector, check only against the coverage length to see if
we have enough data, combine the "should we check the checksum?" check
with the "*can* we check the checksum?" check in a single if, and throw
a dissector assertion if the source network address type isn't IPv4 or
IPv6.
Get rid of inclues of <epan/in_cksum.h> in dissectors that don't use any
of the Internet checksum routines.
In the HIP dissector, make sure we have the data to calculate the
checksum before doing so.
Change-Id: I2f9674775dbb54c533d33082632809f7d32ec8ae
Reviewed-on: https://code.wireshark.org/review/3517
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2014-08-09 01:09:00 +00:00
|
|
|
SET_CKSUM_VEC_TVB(cksum_vec[0], tvb, 0, carp_len);
|
2013-06-14 14:35:34 +00:00
|
|
|
computed_cksum = in_cksum(&cksum_vec[0], 1);
|
|
|
|
if (computed_cksum == 0) {
|
2014-08-01 14:08:57 +00:00
|
|
|
proto_item_append_text(ti, " [correct]");
|
2013-06-14 14:35:34 +00:00
|
|
|
} else {
|
2014-08-01 14:08:57 +00:00
|
|
|
proto_item_append_text(ti, " [incorrect, should be 0x%04x]",
|
2013-06-14 14:35:34 +00:00
|
|
|
in_cksum_shouldbe(cksum, computed_cksum));
|
|
|
|
}
|
|
|
|
}
|
2014-08-01 14:08:57 +00:00
|
|
|
|
2013-06-14 14:35:34 +00:00
|
|
|
offset+=2;
|
|
|
|
|
|
|
|
/* Counter */
|
|
|
|
proto_tree_add_item(carp_tree, hf_carp_counter, tvb, offset, 8, ENC_BIG_ENDIAN);
|
|
|
|
offset+=8;
|
|
|
|
|
2014-09-19 01:18:34 +00:00
|
|
|
proto_tree_add_item(carp_tree, hf_carp_hmac, tvb, offset, 20, ENC_NA);
|
2013-06-14 14:35:34 +00:00
|
|
|
offset+=20;
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* heuristic dissector */
|
|
|
|
static gboolean
|
|
|
|
dissect_carp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
|
2013-06-14 15:08:10 +00:00
|
|
|
{
|
2013-06-14 14:35:34 +00:00
|
|
|
if (!test_carp_packet(tvb, pinfo, tree, data))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
dissect_carp(tvb, pinfo, tree, data);
|
2013-06-14 15:08:10 +00:00
|
|
|
return TRUE;
|
2013-06-14 14:35:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void proto_register_carp(void)
|
|
|
|
{
|
|
|
|
static hf_register_info hf[] = {
|
|
|
|
{ &hf_carp_ver_type,
|
|
|
|
{"CARP message version and type", "carp.typever",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
"CARP version and type", HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_version,
|
|
|
|
{"CARP protocol version", "carp.version",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, CARP_VERSION_MASK,
|
|
|
|
"CARP version", HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_type,
|
|
|
|
{"CARP packet type", "carp.type",
|
|
|
|
FT_UINT8, BASE_DEC, VALS(carp_type_vals), CARP_TYPE_MASK,
|
|
|
|
"CARP type", HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_vhid,
|
|
|
|
{"Virtual Host ID", "carp.vhid",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
NULL, HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_advskew,
|
|
|
|
{"Advertisment Skew", "carp.advskew",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
NULL, HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_authlen,
|
|
|
|
{"Auth Len", "carp.authlen",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
"Size of counter+hash in 32bit chunks", HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_demotion,
|
|
|
|
{"Demotion indicator", "carp.demotion",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
NULL, HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_advbase,
|
|
|
|
{"Adver Int", "carp.adver_int",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
|
|
"Time interval (in seconds) between ADVERTISEMENTS", HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_counter, {"Counter", "carp.counter",
|
|
|
|
FT_UINT64, BASE_DEC, NULL, 0x0,
|
|
|
|
NULL, HFILL }},
|
|
|
|
|
|
|
|
{ &hf_carp_hmac,
|
|
|
|
{"HMAC", "carp.hmac",
|
|
|
|
FT_BYTES, BASE_NONE, NULL, 0x0,
|
|
|
|
"SHA-1 HMAC", HFILL }},
|
2014-08-01 14:08:57 +00:00
|
|
|
|
|
|
|
{ &hf_carp_checksum,
|
|
|
|
{"Checksum", "carp.checksum",
|
|
|
|
FT_UINT16, BASE_HEX, NULL, 0x0,
|
|
|
|
NULL, HFILL }},
|
2013-06-14 14:35:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static gint *ett[] = {
|
|
|
|
&ett_carp,
|
|
|
|
&ett_carp_ver_type
|
|
|
|
};
|
|
|
|
|
|
|
|
proto_carp = proto_register_protocol("Common Address Redundancy Protocol",
|
|
|
|
"CARP", "carp");
|
|
|
|
proto_register_field_array(proto_carp, hf, array_length(hf));
|
|
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
proto_reg_handoff_carp(void)
|
|
|
|
{
|
|
|
|
dissector_handle_t carp_handle;
|
|
|
|
|
2015-12-09 03:49:44 +00:00
|
|
|
carp_handle = create_dissector_handle(dissect_carp, proto_carp);
|
2013-06-14 14:35:34 +00:00
|
|
|
dissector_add_uint("ip.proto", IP_PROTO_VRRP, carp_handle);
|
2015-07-13 00:40:31 +00:00
|
|
|
heur_dissector_add( "ip", dissect_carp_heur, "CARP over IP", "carp_ip", proto_carp, HEURISTIC_ENABLE);
|
2013-06-14 14:35:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
|
|
|
*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
|
|
*/
|