/**************************************************************************** ** 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); } }