/************************************************************************/
/*	Routing Protocol Simulator	Release 1.0	1994/3/17	*/
/*                                              1.21    1997/2/10       */
/*									*/
/*		module 	: protocol procedure	      			*/
/*		file	: icmp_pkt.c			       		*/
/*									*/
/*   Copyright (c) 1994-1997 by Systems Development Laboratory Hitachi,	*/
/*   Ltd. All rights reserved.						*/
/*----------------------------------------------------------------------*/
/*	UPDATE HISTORY							*/
/*									*/
/************************************************************************/
static char copyright[]=
  "@(#)Copyright (c) 1994-1997 by Systems Development Laboratory Hitachi,Ltd.\n All rights researved.\n";

#ifdef USE_ICMP

#include "rps.h"
#include "rps_icmp.h"
#include "netmng.h"
#include "router.h"

extern struct NET_TBL *get_link_net();
extern struct RT_TBL *rt_search();
extern u_long get_dr_addr();
extern u_long get_bdr_addr();
extern u_long get_dr_id();
extern u_long get_bdr_ida();
extern FILE *get_trace_fp();
extern byte *get_rt_table();
extern byte *search_lsdb();
extern struct NET_TBL *get_link_net();
extern void udp_rxpkt();
extern void ospf_rxpkt();
extern void hello_time_out();
extern byte *timer_init();
extern char *time_to_char();

void icmp_packet_dump();
void icmp_echo_dump();
void icmp_unreach_dump();
void icmp_time_out();

int recv_ether_frame();

#define UDP_MAX_SIZE	512
#define ECHO_LEN	64
#define RPS_UDP_PORT	6000

#define IPPROTO_RPS	100

static byte icmp_send_buff[UDP_MAX_SIZE];
static byte recv_buff[UDP_MAX_SIZE*2];

#define ICMP_SEND_ALLOC(ptr,type,size) \
  { \
      bzero(icmp_send_buff,UDP_MAX_SIZE); \
      ptr = (struct type *)icmp_send_buff; \
   }

int icmp_sock = -1;
byte *icmp_send_timer = NULL;
u_long icmp_data[2];
int icmp_seq;
int send_pkt_num = 0;
int recv_wait_flag;

char *icmp_type[] = {"ECHO REPLY",NULL,NULL,"UNREACH","SOURCE QUENCH","REDIRECT",
		     NULL,NULL,"ECHO",NULL,NULL,"TIME EXCEED","PRAM PROBLEM",
		     "TIME STAMP","TIME STAMP REPLY"};

char *icmp_unreach[] = {"Net Unreachable","Host Unreachable",
			"Protocol Unreachable","Port Unreachable",
			"Fragmentation Needed","Source Route Failed"};
/*

  icmp_init

*/
int icmp_init(id,intf)
u_long id;
u_long intf;
{

    if(icmp_sock < 0) {
	icmp_sock = get_socket(SOCK_RAW,IPPROTO_ICMP,NULL);
	ether_init(intf);
	/*	socket_bind(icmp_sock,RPS_UDP_PORT,intf);*/
	icmp_send_timer = timer_init(id,icmp_data,icmp_time_out);
	icmp_seq = 0;
	recv_wait_flag = 1;
    }


}

/*

  icmp_term

*/
int icmp_term(id)
u_long id;
{

    if(icmp_sock > 0) {
	sock_close(icmp_sock);
	icmp_sock = -1;
    }
}


/*

  icmp_message_send

*/
void icmp_message_send(id,type,src,dst,num,interval)
u_long id;
int type;
u_long src;
u_long dst;
int num;
int interval;
{

    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	log_trace(fp,"\n <<< ICMP Message Send >>>\n");
    }

    log_trace(stderr,"\n <<< ICMP Message Send >>>\n");

    icmp_data[0] = src;
    icmp_data[1] = dst;
    
    krl_route_add(id,src,dst);
    switch(type) {
    case ICMP_ECHO :  icmp_send_echo(id,src,dst);
	break;
	default : break;
    }
    krl_route_delete(id,src,dst);

    send_pkt_num = num;
    if(icmp_send_timer && interval)
        timer_set(icmp_send_timer,interval);
}

/*

  icmp_send_echo

*/
int icmp_send_echo(id,src,dst)
u_long id;
u_long src;
u_long dst;
{
    int lock = int_lock();
    struct PROTO_ICMP *icmp;
    
    ICMP_SEND_ALLOC(icmp,PROTO_ICMP,ECHO_LEN);

    icmp->type = ICMP_ECHO;
    icmp->code = 0;

    icmp->echo_id = 0;
    icmp->echo_seq = icmp_seq++;

    icmp->cksum = in_cksum((u_short *)icmp,ECHO_LEN);

    icmp_send_packet(id,(byte *)icmp,ECHO_LEN,src,dst);

    int_unlock(lock);
}

/*

  icmp_send_packet

*/
int icmp_send_packet(id,pkt,pkt_len,src,dst)
u_long id;
byte *pkt;
u_short pkt_len;
u_long src;
u_long dst;
{
    if(get_trace_flag(id))
      icmp_packet_dump(id,pkt,src,dst,"SEND");
    
    ospf_txpkt(icmp_sock,pkt,pkt_len,dst);

    return 1;
}
    
/*

  icmp_message_recv

*/
int icmp_message_recv(id,type,src,dst)
u_long id;
int type;
u_long src;
u_long dst;
{
    u_long p_src,p_dst;
/*    char *packet;*/
    int recv_type;
    int proto_type;
    short send_flag;
    int recv_id;
    FILE *fp;
    u_long recv_intf;

    fp = NULL;
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	log_trace(fp,"\n <<< ICMP Message Recv >>>\n");
    }

    log_trace(stderr,"\n <<< ICMP Message Recv >>>\n");

    recv_type = 1;
    ether_init(src);
    frame_recv_init(src);    
    while(icmp_seq < send_pkt_num) {
	if(proto_type = recv_frame(&p_src,&p_dst,recv_buff)) {
	    if(dst == p_dst || proto_type == IPPROTO_ICMP) {
		recv_type = icmp_recv_packet(id,p_src,p_dst,recv_buff);
	/*	printf("src = %s\n",inetaddr_to_char(p_src));
		printf("dst = %s\n",inetaddr_to_char(p_dst));*/
		if((recv_intf = get_frame_recv_intf()) == src) {
		    if(fp) {
			log_trace(fp," \n Recv interface %s is expected \n",inetaddr_to_char(recv_intf));
		    }
		    log_trace(stderr," \n Recv interface %s is expected \n",inetaddr_to_char(recv_intf));
		}
		else {
		    if(fp) {
			log_trace(fp," \n Recv interface %s is unexpected \n",inetaddr_to_char(recv_intf)); 
		    }
		    log_trace(stderr," \n Recv interface %s is unexpected \n",inetaddr_to_char(recv_intf));
		}
		break;
	    }
	    else if(proto_type == 89){
		recv_id = get_router_id(p_dst);
		(void)ospf_recv_packet(recv_id,NULL,p_src,p_dst,recv_buff);
	    }
	}
    }
    frame_recv_term();

    timer_stop(icmp_send_timer);

    if(type == recv_type) {
	return 1;
    }
    else {
	if(fp) {
	    log_trace(fp,"\n Invalid ICMP packet(%s) received \n",icmp_type[recv_type]);
	}
	log_trace(stderr,"\n Invalid ICMP packet(%s) received \n",icmp_type[recv_type]);
/*	printf("src = %s\n",inetaddr_to_char(p_src));
	printf("dst = %s\n",inetaddr_to_char(p_dst));*/
	return 0;
    }
    
}

/*

  icmp_recv_packet

*/
int icmp_recv_packet(id,src,dst,pkt)
u_long id;
u_long src;
u_long dst;
byte *pkt;
{
    int recv_type;
    struct PROTO_ICMP *icmp;
    
    icmp = (struct PROTO_ICMP *)pkt;
    
    if(id == 0 || get_trace_flag(id))
       icmp_packet_dump(id,pkt,src,dst,"RECV");

    switch(icmp->type) {
    case ICMP_ECHOREPLY :
    case ICMP_ECHO :
    case ICMP_UNREACH :
    case ICMP_SRCQUENCH :
    case ICMP_REDIRECT :
    case ICMP_TIMEEXCEED :
    case ICMP_PRAMPROB :
    case ICMP_TIMESTAMP :
    case ICMP_TIMESTAMPRPLY :
    default : break;
    }

    return icmp->type;
}

      
/*

  icmp_packet_dump

*/
void icmp_packet_dump(id,pkt,src,dst,action)
u_long id;
byte *pkt;
u_long src;
u_long dst;
char *action;
{
    FILE *fp;
    time_t act_time;
    char time_buf[32],all_buff[32];
    struct PROTO_ICMP *icmp;
    
    act_time = get_currnt_time();
    strcpy(all_buff,time_to_char(&act_time));
    strncpy(time_buf,&all_buff[4],15);
    time_buf[15] = '\0';

    icmp = (struct PROTO_ICMP *)pkt;
    
    if(id == 0 || (fp = get_trace_fp(id)) == NULL) fp = stderr;
    log_trace(fp,"\n %-8s %s  %s -> ",
	      icmp_type[icmp->type],action,inetaddr_to_char(src));
    log_trace(fp,"%s at %s\n\n",inetaddr_to_char(dst),time_buf);

    log_trace(stderr,"\n %-8s %s  %s -> ",
	      icmp_type[icmp->type],action,inetaddr_to_char(src));
    log_trace(stderr,"%s at  %s\n\n",inetaddr_to_char(dst),time_buf);

    switch(icmp->type) {
    case ICMP_ECHOREPLY :
    case ICMP_ECHO : icmp_echo_dump(fp,icmp);
		     icmp_echo_dump(stderr,icmp);
		     break;
    case ICMP_UNREACH : icmp_unreach_dump(fp,icmp);
			icmp_unreach_dump(stderr,icmp);
			break;
    case ICMP_SRCQUENCH :
    case ICMP_REDIRECT :
    case ICMP_TIMEEXCEED :
    case ICMP_PRAMPROB :
    case ICMP_TIMESTAMP :
    case ICMP_TIMESTAMPRPLY :
    default : break;
    }
 
}

/*

  icmp_echo_dump

*/
void icmp_echo_dump(fp,icmp)
FILE *fp;
struct PROTO_ICMP *icmp;
{
    log_trace(fp," id = %d , seq = %d\n",icmp->echo_id,icmp->echo_seq);
}

/*

  icmp_unreach_dump

*/
void icmp_unreach_dump(fp,icmp)
FILE *fp;
struct PROTO_ICMP *icmp;
{
    log_trace(fp," %s\n",icmp_unreach[icmp->code]);
}
	
    
/*
  
  icmp_time_out

*/
void icmp_time_out(id,data)
u_long id;
byte *data;
{
    u_long *rt;

/*    if(icmp_seq > send_pkt_num) {
	return ;
    }
  */  
    rt = (u_long *)data;
    krl_route_add(id,*rt,*(rt+1));
    icmp_send_echo(id,*rt,*(rt+1));
    krl_route_delete(id,*rt,*(rt+1));
}

#endif /* USE_ICMP */
