/****************************************************************************
** File: icmp.c
**
** Author: Mike Borella
**
** Comments: Dump ICMP information
**
** $Id: icmp.c,v 1.17 2002/01/03 00:04:01 mborella Exp $
**
** 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/
#include "icmp.h"
#include "ip.h"
#define HOLDER_SIZE 64
/*
* Format of ICMP echo request and reply
*/
typedef struct icmp_echo
{
u_int16_t id;
u_int16_t seqno;
} icmp_echo_t;
/*
* Format of ICMP mask request and reply
*/
typedef struct icmp_mask
{
u_int16_t id;
u_int16_t seqno;
u_int32_t mask;
} icmp_mask_t;
/*
* Format of ICMP router advertisement
*/
typedef struct icmp_routeradvert
{
u_int8_t addresses;
u_int8_t address_size;
u_int16_t lifetime;
} icmp_routeradvert_t;
/*
* Contains the descriptions of ICMP types
*/
strmap_t icmp_type_map[] =
{
{ ICMP_TYPE_ECHOREPLY , "echo reply" },
{ ICMP_TYPE_DESTUNREACHABLE, "destination unreachable" },
{ ICMP_TYPE_SOURCEQUENCH, "source quench" },
{ ICMP_TYPE_REDIRECT, "redirect" },
{ ICMP_TYPE_ECHOREQUEST, "echo request" },
{ ICMP_TYPE_ROUTERADVERT, "router advertisement" },
{ ICMP_TYPE_ROUTERSOLICIT, "router solicitation" },
{ ICMP_TYPE_TIMEEXCEEDED, "time exceeded" },
{ ICMP_TYPE_PARAMPROBLEM, "parameter problem" },
{ ICMP_TYPE_TIMESTAMP, "timestamp" },
{ ICMP_TYPE_TIMESTAMPREPLY, "timestamp reply" },
{ ICMP_TYPE_INFOREQUEST, "information request" },
{ ICMP_TYPE_INFOREPLY, "information reply" },
{ ICMP_TYPE_MASKREQUEST, "mask request" },
{ ICMP_TYPE_MASKREPLY, "mask reply" },
{ ICMP_TYPE_TRACEROUTE, "traceroute" },
{ ICMP_TYPE_CONVERSIONERROR, "datagram conversion error" },
{ 0, "" }
};
/*
* Contains the descriptions of ICMP destination unreachable messages
*/
strmap_t icmp_du_map[] =
{
{ ICMP_DU_NET, "network unreachable" },
{ ICMP_DU_HOST, "host unreachable" },
{ ICMP_DU_PROTOCOL, "protocol unreachable" },
{ ICMP_DU_PORT, "port unreachable"},
{ ICMP_DU_FRAG, "fragmentation needed"},
{ ICMP_DU_SRCRTEFAIL, "source route failed"},
{ ICMP_DU_NETUNKNOWN, "network unknown"},
{ ICMP_DU_HOSTUNKNOWN, "host unknown"},
{ ICMP_DU_SRCISOLATED, "source isolated"},
{ ICMP_DU_NETADMIN, "network admin prohibited"},
{ ICMP_DU_HOSTADMIN, "host admin prohibited"},
{ ICMP_DU_NETTOS, "network unreachable for TOS"},
{ ICMP_DU_HOSTTOS, "host unreachable for TOS"},
{ ICMP_DU_ADMIN, "admin prohibited"},
{ ICMP_DU_HOSTPRECVIOL, "host precedence violation"},
{ ICMP_DU_PRECCUTOFF, "precendence cutoff"},
{ 0, "" }
};
/*
* Contains the descriptions of ICMP destination unreachable messages
*/
strmap_t icmp_timeexceeded_map[] =
{
{ ICMP_TIMEEXCEEDED_TTL, "TTL equals 0" },
{ ICMP_TIMEEXCEEDED_REASSEMBLY, "TTL expired in reassembly"}
};
extern struct arg_t * my_args;
/*----------------------------------------------------------------------------
**
** dump_icmp_echo()
**
** Parse ICMP echo request and echo reply fields. Note that we don't have to
** check for the -n option because if it is set, we shouldn't ever get here.
**
**----------------------------------------------------------------------------
*/
void dump_icmp_echo(packet_t *pkt)
{
icmp_echo_t echo;
/*
* Get the Echo Request/Reply fields
*/
if (get_packet_bytes((u_int8_t *) &echo, pkt, 4) == 0)
return;
/*
* Dump the ID and sequence #
*/
if (my_args->m)
{
display_minimal((u_int8_t *) &echo.seqno, 2, DISP_DEC);
display_minimal_string(" ");
}
else
{
display("Identifier", (u_int8_t *) &echo.id, 2, DISP_DEC);
display("Sequence number", (u_int8_t *) &echo.seqno, 2, DISP_DEC);
}
}
/*----------------------------------------------------------------------------
**
** dump_icmp_mask()
**
** Parse ICMP mask request and reply fields
**
**----------------------------------------------------------------------------
*/
void dump_icmp_mask(packet_t *pkt)
{
icmp_mask_t mask;
/*
* Get the Mask Request/Reply fields
*/
if (get_packet_bytes((u_int8_t *) &mask, pkt, 8) == 0)
return;
/*
* Dump the mask
*/
if (my_args->m)
{
display_minimal_ipv4((u_int8_t *) &mask.mask);
}
else
{
display_ipv4("Mask", (u_int8_t *) &mask.mask);
}
}
/*----------------------------------------------------------------------------
**
** dump_icmp_routeradvert()
**
** Parse ICMP router advertisement
**
**----------------------------------------------------------------------------
*/
void dump_icmp_routeradvert(packet_t *pkt)
{
icmp_routeradvert_t ra;
u_int32_t addr;
u_int32_t preference;
int i;
/*
* Get the basic fields
*/
if (get_packet_bytes((u_int8_t *) &ra, pkt, sizeof(ra)) == 0)
return;
/* Conversions */
ra.lifetime = ntohs(ra.lifetime);
/*
* Dump the basic fields
*/
if (my_args->m)
{
}
else
{
display("Addresses", (u_int8_t *) &ra.addresses, 1, DISP_DEC);
display("Address size", (u_int8_t *) &ra.address_size, 1, DISP_DEC);
display("Lifetime", (u_int8_t *) &ra.lifetime, 2, DISP_DEC);
}
/*
* Loop through addresses
*/
for (i=0; i < ra.addresses; i++)
{
if (get_packet_bytes((u_int8_t *) &addr, pkt, 4) == 0)
return;
if (get_packet_bytes((u_int8_t *) &preference, pkt, 4) == 0)
return;
/* Conversions */
preference = ntohl(preference);
if (my_args->m)
{
display_minimal_ipv4((u_int8_t *) &addr);
display_minimal_string("(");
display_minimal((u_int8_t *) &preference, 4, DISP_DEC);
display_minimal_string(") ");
}
else
{
display_ipv4("Address", (u_int8_t *) &addr);
display("Preference", (u_int8_t *) &preference, 4, DISP_DEC);
}
}
}
/*----------------------------------------------------------------------------
**
** dump_icmp()
**
** Parse ICMP header and dump fields
**
**----------------------------------------------------------------------------
*/
void dump_icmp(packet_t *pkt)
{
icmp_header_t icmp;
char holder[HOLDER_SIZE];
/* Set the layer */
set_layer(LAYER_NETWORK);
/*
* Stats accounting
*/
stats_update(STATS_ICMP);
/*
* Get the header
*/
if (get_packet_bytes((u_int8_t *) &icmp, pkt, 4) == 0)
return;
if (my_args->m)
{
/* In minimal mode lets just dump the type and code */
display_minimal_string("| ICMP ");
display_minimal_string(map2str(icmp_type_map, icmp.type));
display_minimal_string(" ");
switch(icmp.type)
{
case ICMP_TYPE_DESTUNREACHABLE:
display_minimal_string(map2str(icmp_du_map, icmp.code));
break;
case ICMP_TYPE_TIMEEXCEEDED:
display_minimal_string(map2str(icmp_timeexceeded_map, icmp.code));
break;
case ICMP_TYPE_ECHOREPLY:
case ICMP_TYPE_ECHOREQUEST:
dump_icmp_echo(pkt);
break;
case ICMP_TYPE_MASKREQUEST:
case ICMP_TYPE_MASKREPLY:
dump_icmp_mask(pkt);
break;
case ICMP_TYPE_ROUTERADVERT:
dump_icmp_routeradvert(pkt);
break;
case ICMP_TYPE_ROUTERSOLICIT:
return;
case ICMP_TYPE_SOURCEQUENCH:
case ICMP_TYPE_REDIRECT:
case ICMP_TYPE_PARAMPROBLEM:
case ICMP_TYPE_TIMESTAMP:
case ICMP_TYPE_TIMESTAMPREPLY:
case ICMP_TYPE_INFOREQUEST:
case ICMP_TYPE_INFOREPLY:
case ICMP_TYPE_TRACEROUTE:
case ICMP_TYPE_CONVERSIONERROR:
break;
default:
break;
}
}
else
{
/* announcement */
display_header_banner("ICMP Header");
/* Dump the type */
snprintf(holder, HOLDER_SIZE, "%d (%s)", icmp.type,
map2str(icmp_type_map, icmp.type));
display("Type", (u_int8_t *) holder, strlen(holder), DISP_STRING);
/*
* Based on the type, dump the code, if the type has codes
*/
switch(icmp.type)
{
case ICMP_TYPE_DESTUNREACHABLE:
snprintf(holder, HOLDER_SIZE, "%d (%s)", icmp.code,
map2str(icmp_du_map, icmp.code));
display("Code", (u_int8_t *) &holder, strlen(holder), DISP_STRING);
break;
case ICMP_TYPE_TIMEEXCEEDED:
snprintf(holder, HOLDER_SIZE, "%d (%s)", icmp.code,
map2str(icmp_timeexceeded_map, icmp.code));
display("Code", (u_int8_t *) &holder, strlen(holder), DISP_STRING);
break;
default:
display("Code", (u_int8_t *) &icmp.code, 1, DISP_DEC);
break;
}
icmp.checksum = ntohs(icmp.checksum);
display("Checksum", (u_int8_t *) &icmp.checksum, 2, DISP_DEC);
/*
* Call an ICMP-type-specific function
*/
switch (icmp.type)
{
case ICMP_TYPE_ECHOREPLY:
case ICMP_TYPE_ECHOREQUEST:
dump_icmp_echo(pkt);
/* dump the hex buffer */
hexbuffer_flush();
return; /* bail so that we don't continue reading */
case ICMP_TYPE_DESTUNREACHABLE:
/* Get the unused bytes */
if (get_packet_bytes((u_int8_t *) &holder, pkt, 4) == 0)
return;
break;
case ICMP_TYPE_ROUTERSOLICIT:
/* Get the unused bytes */
if (get_packet_bytes((u_int8_t *) &holder, pkt, 4) == 0)
return;
/* dump the hex buffer */
hexbuffer_flush();
return;
case ICMP_TYPE_ROUTERADVERT:
dump_icmp_routeradvert(pkt);
/* dump the hex buffer */
hexbuffer_flush();
return;
case ICMP_TYPE_SOURCEQUENCH:
case ICMP_TYPE_REDIRECT:
case ICMP_TYPE_PARAMPROBLEM:
case ICMP_TYPE_TIMESTAMP:
case ICMP_TYPE_TIMESTAMPREPLY:
case ICMP_TYPE_INFOREQUEST:
case ICMP_TYPE_INFOREPLY:
case ICMP_TYPE_TRACEROUTE:
case ICMP_TYPE_CONVERSIONERROR:
break;
case ICMP_TYPE_TIMEEXCEEDED:
/* Get the unused bytes */
if (get_packet_bytes((u_int8_t *) &holder, pkt, 4) == 0)
return;
break;
case ICMP_TYPE_MASKREQUEST:
case ICMP_TYPE_MASKREPLY:
dump_icmp_mask(pkt);
break;
default:
break;
}
/* dump the hex buffer */
hexbuffer_flush();
/*
* In most ICMP packets there will be an IP header after the ICMP hdr.
* If it seems to be there, we can parse it out
*/
if (get_packet_apparentbytesleft(pkt) >= 20)
dump_ip(pkt);
}
}
syntax highlighted by Code2HTML, v. 0.9.1