/**************************************************************************** 
** File: rip.c
**
** Author: Mike Borella
**
** Comments: Dump RIP header information. 
**
** $Id: rip.c,v 1.14 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 "global.h"
#include "rip.h"

#define HOLDER_SIZE 64

/*
 * RIP command map
 */

strmap_t rip_command_map [] =
{
  { RIP_CMD_RQ,          "request" },
  { RIP_CMD_RP,          "reply" },
  { RIP_CMD_POLL,        "poll" },
  { RIP_CMD_POLL_ENTRY,  "poll entry" },
  { 0, "" }
};

extern struct arg_t * my_args;

/*----------------------------------------------------------------------------
**
** dump_rip()
**
** Parse RIP packet and dump fields
**
**----------------------------------------------------------------------------
*/

void dump_rip(packet_t *pkt)
{
  ripv1_header_t       ripv1;
  ripv1_route_header_t ripv1_route;
  ripv2_header_t       ripv2;
  ripv2_route_header_t ripv2_route;
  ripv2_auth_header_t  ripv2_auth;
  char                 holder[HOLDER_SIZE];
  u_int16_t            address_family;
  int route_count;

  /* Set the layer */
  set_layer(LAYER_APPLICATION);

  /* 
   * Look at the version to determine whether to use v1 or v2
   */

  if (look_packet_bytes((u_int8_t *) &ripv1, pkt, sizeof(ripv1_header_t)) == 0)
    return;

  if (ripv1.version == 1)
    {
      /* RIP v1 processing here */

      /*
       * Get the RIP v1 static header
       */
      
      if (get_packet_bytes((u_int8_t *) &ripv1, pkt, sizeof(ripv1_header_t)) 
	  == 0)
	return;

      /*
       * Dump header
       */
      
      if (my_args->m)
	{
	  display_minimal_string("| RIPv1 ");
	  display_minimal_string(map2str(rip_command_map, ripv1.command));
	  display_minimal_string(" ");
	}
      else
	{
	  /* announcement */
	  display_header_banner("RIPv1 Header");
	  
	  snprintf(holder, HOLDER_SIZE, "%d (%s)", ripv1.command, 
		   map2str(rip_command_map, ripv1.command));
	  display_string("Command", holder);
	  display("Version", (u_int8_t *) &ripv1.version, 1, DISP_DEC);
	  display("MBZ", (u_int8_t *) &ripv1.mbz, 2, DISP_DEC);
	}
      
      /*
       * Do the individual routes.
       */
      
      route_count = 0;
      while(1)
	{
	  if (get_packet_bytes((u_int8_t *) &ripv1_route, pkt, 
			       sizeof(ripv1_route_header_t)) == 0)
	    break;
	  
	  /*
	   * Conversions
	   */
	  
	  ripv1_route.addr_fam = ntohs(ripv1_route.addr_fam);
	  ripv1_route.metric = ntohl(ripv1_route.metric);

	  /*
	   * Dump route header
	   */
	  
	  if (my_args->m)
	    {
	      /* for minimal mode, we'll just count the routes */
	      route_count++;
	    }
	  else
	    {
	      display("Address family", (u_int8_t *) &ripv1_route.addr_fam,
		      2, DISP_DEC);
	      display("MBZ", (u_int8_t *) &ripv1_route.mbz1, 2, 
		      DISP_DEC);
	      display_ipv4("IP address", (u_int8_t *) &ripv1_route.ipaddr);
	      display("MBZ", (u_int8_t *) &ripv1_route.mbz2, 8, 
		      DISP_HEX);
	      display("Metric", (u_int8_t *) &ripv1_route.metric, 4, 
		      DISP_DEC);
	    }
	  
	}
      
    }
  else
    {
      /* RIP v2 processing here */
  
      /*
       * Get the RIP v2 static header
       */
      
      if (get_packet_bytes((u_int8_t *) &ripv2, pkt, sizeof(ripv2_header_t)) 
	  == 0)
	return;

      /*
       * Conversions
       */
      
      ripv2.domain = ntohs(ripv2.domain);

      /*
       * Dump header
       */
      
      if (my_args->m)
	{
	  display_minimal_string("| RIPv2 ");
	  display_minimal_string(map2str(rip_command_map, ripv2.command));
	  display_minimal_string(" ");
	}
      else
	{
	  /* announcement */
	  display_header_banner("RIPv2 Header");
	  
	  snprintf(holder, HOLDER_SIZE, "%d (%s)", ripv2.command, 
		   map2str(rip_command_map, ripv2.command));
	  display_string("Command", holder);
	  display("Version", (u_int8_t *) &ripv2.version, 1, DISP_DEC);
	  display("Domain", (u_int8_t *) &ripv2.domain, 2, DISP_DEC);
	}
      
      /*
       * Do the individual routes.
       * First look at the address family to determine the header type.
       */
      
      route_count = 0;
      while (1)
	{
	  if (look_packet_bytes((u_int8_t *) &address_family, pkt, 2) == 0)
	    break;;
      
	  /*
	   * If its an auth header, deal with it - otherwise its a regular
	   * route header 
	   */
	  
	  if (address_family == 0xffff)
	    {
	      if (get_packet_bytes((u_int8_t *) &ripv2_auth, pkt, 
				   sizeof(ripv2_auth_header_t)) == 0)
		return;
	      
	      /*
	       * Conversions
	       */
	      
	      ripv2_auth.addr_fam = ntohs(ripv2_auth.addr_fam);
	      ripv2_auth.type = ntohs(ripv2_auth.type);
	  
	      /*
	       * Dump auth header
	       */
	      
	      if (my_args->m)
		{
		  /* not sure what to do here... */
		}
	      else
		{
		  display("Address family", (u_int8_t *) &ripv2_auth.addr_fam, 
			  2, DISP_DEC);
		  display("Type", (u_int8_t *) &ripv2_auth.type, 2, 
			  DISP_DEC);
		  display_string("Password", ripv2_auth.passwd);
		}
	    }
	  else
	    {
	      if (get_packet_bytes((u_int8_t *) &ripv2_route, pkt, 
				   sizeof(ripv2_route_header_t)) == 0)
		return;
	      
	      /*
	       * Conversions
	       */
	      
	      ripv2_route.addr_fam = ntohs(ripv2_route.addr_fam);
	      ripv2_route.route_tag = ntohs(ripv2_route.route_tag);
	      ripv2_route.metric = ntohl(ripv2_route.metric);

	      /*
	       * Dump route header
	       */
	      
	      if (my_args->m)
		{
		  /* just count the routes for minimal mode */
		  route_count++;
		}
	      else
		{
		  display("Address family", (u_int8_t *) &ripv2_route.addr_fam,
			  2, DISP_DEC);
		  display("Route tag", (u_int8_t *) &ripv2_route.route_tag, 2, 
			  DISP_DEC);
		  display_ipv4("IP address", (u_int8_t *) &ripv2_route.ipaddr);
		  display_ipv4("Netmask", (u_int8_t *) &ripv2_route.netmask);
		  display_ipv4("Next hop", (u_int8_t *) &ripv2_route.next_hop);
		  display("Metric", (u_int8_t *) &ripv2_route.metric, 4, 
			  DISP_DEC);
		}
	      
	    }
	} /* while */
    }
  
  /* display routes for minimal mode */
  if (my_args->m)
    {
      display_minimal((u_int8_t *) &route_count, 4, DISP_DEC);
      display_minimal_string(" routes ");
    }
}
