/*
 * The olsr.org Optimized Link-State Routing daemon(olsrd)
 * Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 * * Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright 
 *   notice, this list of conditions and the following disclaimer in 
 *   the documentation and/or other materials provided with the 
 *   distribution.
 * * Neither the name of olsr.org, olsrd nor the names of its 
 *   contributors may be used to endorse or promote products derived 
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Visit http://www.olsr.org for more information.
 *
 * If you find this software useful feel free to make a donation
 * to the project. For more information see the website or contact
 * the copyright holders.
 *
 * $Id: print_packet.c,v 1.12 2007/09/17 22:14:07 bernd67 Exp $
 */

#include "print_packet.h"
#include "mantissa.h"
#include "defs.h"
#include "olsr.h"
#include "lq_packet.h"

static void
print_messagedump(FILE *, olsr_u8_t *, olsr_16_t);

static void
print_midmsg(FILE *, olsr_u8_t *, olsr_16_t);

static void
print_hnamsg(FILE *, olsr_u8_t *, olsr_16_t);

static void
print_olsr_tcmsg(FILE *, olsr_u8_t *, olsr_16_t);

static void
print_olsr_tcmsg_lq(FILE *, olsr_u8_t *, olsr_16_t);

static void
print_hellomsg(FILE *, olsr_u8_t *, olsr_16_t);

static void
print_hellomsg_lq(FILE *, olsr_u8_t *, olsr_16_t);

/* Entire packet */
olsr_8_t
print_olsr_serialized_packet(FILE *handle, union olsr_packet *pkt, 
			     olsr_u16_t size, union olsr_ip_addr *from_addr)
{
  olsr_16_t remainsize = size - OLSR_HEADERSIZE;
  union olsr_message *msg;

  /* Print packet header (no IP4/6 difference) */
  fprintf(handle, "  ============== OLSR PACKET ==============\n   source: %s\n   length: %d bytes\n   seqno: %d\n\n",
	  from_addr ? olsr_ip_to_string(from_addr) : "UNKNOWN",
	  ntohs(pkt->v4.olsr_packlen), ntohs(pkt->v4.olsr_seqno));

  /* Check size */
  if(size != ntohs(pkt->v4.olsr_packlen))
    fprintf(handle, "   SIZE MISSMATCH(%d != %d)!\n", size, ntohs(pkt->v4.olsr_packlen));

  msg = (union olsr_message *)pkt->v4.olsr_msg;

  /* Print all messages */
  while((remainsize > 0) && ntohs(msg->v4.olsr_msgsize))
    {
      print_olsr_serialized_message(handle, msg);
      remainsize -= ntohs(msg->v4.olsr_msgsize);
      msg = (union olsr_message *)((char *)msg + ntohs(msg->v4.olsr_msgsize));
    }

  /* Done */
  fprintf(handle, "  =========================================\n\n");
  return 1;
}

/* Single message */
olsr_8_t
print_olsr_serialized_message(FILE *handle, union olsr_message *msg)
{

  fprintf(handle, "   ------------ OLSR MESSAGE ------------\n");
  fprintf(handle, "    Sender main addr: %s\n", 
	  olsr_ip_to_string((union olsr_ip_addr *)&msg->v4.originator));
  fprintf(handle, "    Type: %s, size: %d, vtime: %0.2f\n", 
	  olsr_msgtype_to_string(msg->v4.olsr_msgtype), 
	  ntohs(msg->v4.olsr_msgsize),
	  me_to_double(msg->v4.olsr_vtime));
  fprintf(handle, "    TTL: %d, Hopcnt: %d, seqno: %d\n",
	  (olsr_cnf->ip_version == AF_INET) ? msg->v4.ttl : msg->v6.ttl,
	  (olsr_cnf->ip_version == AF_INET) ? msg->v4.hopcnt : msg->v6.hopcnt,
	  ntohs((olsr_cnf->ip_version == AF_INET) ? msg->v4.seqno : msg->v6.seqno));

  switch(msg->v4.olsr_msgtype)
    {
      /* Print functions for individual messagetypes */
    case(MID_MESSAGE):
      print_midmsg(handle,
		   (olsr_cnf->ip_version == AF_INET) ? 
		   (olsr_u8_t *)&msg->v4.message : (olsr_u8_t *)&msg->v6.message,
		   ntohs(msg->v4.olsr_msgsize));
      break;
    case(HNA_MESSAGE):
      print_hnamsg(handle,
		   (olsr_cnf->ip_version == AF_INET) ? 
		   (olsr_u8_t *)&msg->v4.message : (olsr_u8_t *)&msg->v6.message,
		   ntohs(msg->v4.olsr_msgsize));
      break;
    case(TC_MESSAGE):
      print_olsr_tcmsg(handle,
		  (olsr_cnf->ip_version == AF_INET) ? 
		  (olsr_u8_t *)&msg->v4.message : (olsr_u8_t *)&msg->v6.message,
		  ntohs(msg->v4.olsr_msgsize));
      break;
    case(LQ_TC_MESSAGE):
      print_olsr_tcmsg_lq(handle,
		     (olsr_cnf->ip_version == AF_INET) ? 
		     (olsr_u8_t *)&msg->v4.message : (olsr_u8_t *)&msg->v6.message,
		     ntohs(msg->v4.olsr_msgsize));
      break;
    case(HELLO_MESSAGE):
      print_hellomsg(handle,
		     (olsr_cnf->ip_version == AF_INET) ? 
		     (olsr_u8_t *)&msg->v4.message : (olsr_u8_t *)&msg->v6.message,
		     ntohs(msg->v4.olsr_msgsize));
      break;
    case(LQ_HELLO_MESSAGE):
      print_hellomsg_lq(handle,
			(olsr_cnf->ip_version == AF_INET) ? 
			(olsr_u8_t *)&msg->v4.message : (olsr_u8_t *)&msg->v6.message,
		     ntohs(msg->v4.olsr_msgsize));
      break;
    default:
      print_messagedump(handle, (olsr_u8_t *)msg, ntohs(msg->v4.olsr_msgsize));
    }

  fprintf(handle, "   --------------------------------------\n\n");
  return 1;
}


static void
print_messagedump(FILE *handle, olsr_u8_t *msg, olsr_16_t size)
{
  int i, x = 0;

  fprintf(handle, "     Data dump:\n     ");
  for(i = 0; i < size; i++)
    {
      if(x == 4)
	{
	  x = 0;
	  fprintf(handle, "\n     ");
	}
      x++;
      if(olsr_cnf->ip_version == AF_INET)
	fprintf(handle, " %-3i ", (u_char) msg[i]);
      else
	fprintf(handle, " %-2x ", (u_char) msg[i]);
    }
  fprintf(handle, "\n");
}


static void
print_hellomsg(FILE *handle, olsr_u8_t *data, olsr_16_t totsize)
{
  union olsr_ip_addr *haddr;
  int hellosize = totsize - ((olsr_cnf->ip_version == AF_INET) ? OLSR_MSGHDRSZ_IPV4 : OLSR_MSGHDRSZ_IPV6);

  fprintf(handle, "    +Htime: %0.2f\n", me_to_double(data[2]));

  fprintf(handle, "    +Willingness: %d\n", data[3]);

  if(olsr_cnf->ip_version == AF_INET)
    {
      /* IPv4 */
      struct hellomsg *h;
      struct hellinfo *hinf;

      h = (struct hellomsg *)data;

      for (hinf = h->hell_info; 
	   (char *)hinf < ((char *)data + hellosize); 
	   hinf = (struct hellinfo *)((char *)hinf + ntohs(hinf->size)))
	{

	  fprintf(handle, "    ++ Link: %s, Status: %s, Size: %d\n", 
		  olsr_link_to_string(EXTRACT_LINK(hinf->link_code)), 
		  olsr_status_to_string(EXTRACT_STATUS(hinf->link_code)),
		  ntohs(hinf->size));

	  for (haddr = (union olsr_ip_addr  *)&hinf->neigh_addr; 
	       (char *)haddr < (char *)hinf + ntohs(hinf->size); 
	       haddr = (union olsr_ip_addr *)&haddr->v6.s6_addr[4])
	    {

	      fprintf(handle, "    ++ %s\n", olsr_ip_to_string(haddr));
	    }
	}

      
    }
  else
    {
      /* IPv6 */
      struct hellomsg6 *h6;
      struct hellinfo6 *hinf6;

      h6 = (struct hellomsg6 *)data;

      for (hinf6 = h6->hell_info; (char *)hinf6 < ((char *)data + (hellosize)); 
	   hinf6 = (struct hellinfo6 *)((char *)hinf6 + ntohs(hinf6->size)))
	{
	  fprintf(handle, "    ++ Link: %s, Status: %s, Size: %d\n", 
		  olsr_link_to_string(EXTRACT_LINK(hinf6->link_code)), 
		  olsr_status_to_string(EXTRACT_STATUS(hinf6->link_code)),
		  ntohs(hinf6->size));

	  for (haddr = (union olsr_ip_addr *)hinf6->neigh_addr; 
	       (char *)haddr < (char *)hinf6 + ntohs(hinf6->size); 
	       haddr++)
	    {
	      fprintf(handle, "    ++ %s\n", olsr_ip_to_string(haddr));
	    }
	}

    }

}

static void
print_hellomsg_lq(FILE *handle, olsr_u8_t *data, olsr_16_t totsize)
{
  union olsr_ip_addr *haddr;
  int hellosize = totsize - ((olsr_cnf->ip_version == AF_INET) ? OLSR_MSGHDRSZ_IPV4 : OLSR_MSGHDRSZ_IPV6);

  fprintf(handle, "    +Htime: %0.2f\n", me_to_double(data[2]));

  fprintf(handle, "    +Willingness: %d\n", data[3]);

  if(olsr_cnf->ip_version == AF_INET)
    {
      /* IPv4 */
      struct hellomsg *h;
      struct hellinfo *hinf;

      h = (struct hellomsg *)data;

      for (hinf = h->hell_info; 
	   (char *)hinf < ((char *)data + hellosize); 
	   hinf = (struct hellinfo *)((char *)hinf + ntohs(hinf->size)))
	{

	  fprintf(handle, "    ++ Link: %s, Status: %s, Size: %d\n", 
		  olsr_link_to_string(EXTRACT_LINK(hinf->link_code)), 
		  olsr_status_to_string(EXTRACT_STATUS(hinf->link_code)),
		  ntohs(hinf->size));

	  for (haddr = (union olsr_ip_addr  *)&hinf->neigh_addr; 
	       (char *)haddr < (char *)hinf + ntohs(hinf->size); 
	       haddr = (union olsr_ip_addr *)&haddr->v6.s6_addr[8])
	    {
	      olsr_u8_t *quality = (olsr_u8_t *)haddr + olsr_cnf->ipsize;
	      fprintf(handle, "    ++ %s\n", olsr_ip_to_string(haddr));
	      fprintf(handle, "    ++ LQ = %d, RLQ = %d\n", quality[0], quality[1]);
	    }
	}

      
    }
  else
    {
      /* IPv6 */
      struct hellomsg6 *h6;
      struct hellinfo6 *hinf6;

      h6 = (struct hellomsg6 *)data;

      for (hinf6 = h6->hell_info; (char *)hinf6 < ((char *)data + (hellosize)); 
	   hinf6 = (struct hellinfo6 *)((char *)hinf6 + ntohs(hinf6->size)))
	{
	  fprintf(handle, "    ++ Link: %s, Status: %s, Size: %d\n", 
		  olsr_link_to_string(EXTRACT_LINK(hinf6->link_code)), 
		  olsr_status_to_string(EXTRACT_STATUS(hinf6->link_code)),
		  ntohs(hinf6->size));

	  for (haddr = (union olsr_ip_addr *)hinf6->neigh_addr; 
	       (char *)haddr < (char *)hinf6 + ntohs(hinf6->size) + 4; 
	       haddr++)
	    {
	      olsr_u8_t *quality = (olsr_u8_t *)haddr + olsr_cnf->ipsize;
	      fprintf(handle, "    ++ %s\n", olsr_ip_to_string(haddr));
	      fprintf(handle, "    ++ LQ = %d, RLQ = %d\n", quality[0], quality[1]);
	    }
	}

    }
}

static void
print_olsr_tcmsg_lq(FILE *handle, olsr_u8_t *data, olsr_16_t totsize)
{
  int remsize = totsize - ((olsr_cnf->ip_version == AF_INET) ? OLSR_MSGHDRSZ_IPV4 : OLSR_MSGHDRSZ_IPV6);
  
  fprintf(handle, "    +ANSN: %d\n", htons(((struct olsr_tcmsg *)data)->ansn));

  data += 4;
  remsize -= 4;

  while(remsize)
    {
      fprintf(handle, "    +Neighbor: %s\n", olsr_ip_to_string((union olsr_ip_addr *) data));
      data += olsr_cnf->ipsize;
      fprintf(handle, "    +LQ: %d, ", *data);
      data += 1;
      fprintf(handle, "RLQ: %d\n", *data);
      data += 3;
      remsize -= (olsr_cnf->ipsize + 4);
    }

}


static void
print_olsr_tcmsg(FILE *handle, olsr_u8_t *data, olsr_16_t totsize)
{
  int remsize = totsize - ((olsr_cnf->ip_version == AF_INET) ? OLSR_MSGHDRSZ_IPV4 : OLSR_MSGHDRSZ_IPV6);
  
  fprintf(handle, "    +ANSN: %d\n", htons(((struct olsr_tcmsg *)data)->ansn));

  data += 4;
  remsize -= 4;

  while(remsize)
    {
      fprintf(handle, "    +Neighbor: %s\n", olsr_ip_to_string((union olsr_ip_addr *) data));
      data += olsr_cnf->ipsize;

      remsize -= olsr_cnf->ipsize;
    }

}


static void
print_hnamsg(FILE *handle, olsr_u8_t *data, olsr_16_t totsize)
{
  int remsize = totsize - ((olsr_cnf->ip_version == AF_INET) ? OLSR_MSGHDRSZ_IPV4 : OLSR_MSGHDRSZ_IPV6);

  while(remsize)
    {
      fprintf(handle, "    +Network: %s\n", olsr_ip_to_string((union olsr_ip_addr *) data));
      data += olsr_cnf->ipsize;
      fprintf(handle, "    +Netmask: %s\n", olsr_ip_to_string((union olsr_ip_addr *) data));
      data += olsr_cnf->ipsize;

      remsize -= (olsr_cnf->ipsize*2);
    }

}

static void
print_midmsg(FILE *handle, olsr_u8_t *data, olsr_16_t totsize)
{
  int remsize = totsize - ((olsr_cnf->ip_version == AF_INET) ? OLSR_MSGHDRSZ_IPV4 : OLSR_MSGHDRSZ_IPV6);

  while(remsize)
    {
      fprintf(handle, "    +Alias: %s\n", olsr_ip_to_string((union olsr_ip_addr *) data));
      data += olsr_cnf->ipsize;
      remsize -= olsr_cnf->ipsize;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1