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

#include "rps.h"
#include "rps_token.h"
#include "router.h"
#include "rps_ospf.h"
#include "ospf_type.h"

#define MAX_AGE		3600

void ospf_message_send();
void ospf_message_recv();
void ospf_nbr_state_adjok();
void ospf_if_state_ifup();
void ospf_if_state_nbrchange();
void ospf_nbr_state_loading();
void ospf_flood();

extern FILE *get_trace_fp();
extern char *get_rt_table();
extern byte *get_proto_info();
extern char *make_lsdb();
extern struct RT_IFCONF *get_local_rtif();
extern char *inetaddr_to_char();
extern char *lsdb_load();
extern byte *make_lsdb_sum_list();

char *ospf_nbr_event[] =
{	"Hello Received",
	"Start",
	"2-Way Received",
	"Adjacency OK",
	"Negotiation Done",
	"Exchange Done",
	"Sequence Number Mismatch",
	"Bad Ls Request",
	"Loading Done",
	"1-Way",
	"Reset Adjacency",
	"Kill Neighbor",
	"Inactivity Timer",
	"Lower Level Down"
};

char *ospf_nbr_state[] =
{	"Down",
	"Attempt",
	"Init",
	"2-Way",
	"ExStart",
	"Exchange",
	"Loading",
	"Full",
	"Scvirtual"
};

char *ospf_if_event[] =
{	"Interface Up",
	"Wait Timer",
	"Backup Seen",
        "Neighbor Change",
	"Loop Indication",
	"Unloop Indication",
	"Interface Down"
};

char *ospf_if_state[] = 
{	"Down",
	"Loopback",
	"Waiting",
	"Point to Point",
	"Dr",
	"Backup Dr",
	"Dr Other"
};
	
static char pkt_buff[1500];
/*

  ospf_message_send

*/
void ospf_message_send(rtif,type)
struct RT_IFCONF *rtif;
int type;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_NBR *nbr;
    struct OSPF_IF *intf;
    u_long id;
    int send_flag;
    
    if(rtif->self->trace_flag) {
	log_trace(rtif->self->trace_fp,"\n <<< OSPF Message Send >>>\n");
    }

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

    ospf = (struct PROTO_OSPF *)rtif->proto_info;
    intf = ospf->myarea->intf;
    nbr = intf->nbr;
    id = rtif->self->router_id;

    send_flag = intf->send_flag;
    
    intf->send_flag = SEND_OK;
    
    switch(type) {
    case OSPF_HELLO_PKT : if(nbr->state < NBR_ATTEMPT) {
				set_dr(id,nbr,intf);
    				ospf_if_state_ifup(id,intf);
			    }
			  else {
			      ospf_send_hello(id,nbr,intf);
			  }
	break;
    case OSPF_DB_PKT : if(nbr->sum_list == NULL) {
		  	  dbsum_list(id,nbr,intf);
			  ospf_send_database(id,nbr,intf);
			  nbr->sum_list = NULL; 
		      }
		      else {
			  ospf_send_database(id,nbr,intf);
		      }
			break;
    case OSPF_LS_REQ_PKT :  if(nbr->req_list == NULL) {
			    	make_lsdb_sum_list(id,rtif->rt_table,nbr->nbr_id);
				lsreq_list(id,NULL,nbr,intf,0);
				ospf_send_req(id,nbr,intf);
			        REQ_LIST_FREE(nbr->req_list);
			        nbr->req_list = NULL;
			    }
			    else {
				ospf_send_req(id,nbr,intf);
			    }
			    break;
    case OSPF_LS_UPDATE_PKT : if(nbr->sum_list == NULL) {
				make_lsdb_sum_list(id,rtif->rt_table,nbr->nbr_id);
				ospf_send_update(id,nbr,intf,rtif->rt_table);
			    }
			    else {
     				ospf_send_update(id,nbr,intf,rtif->rt_table);
			    }
			    break;
    case OSPF_LS_ACK_PKT : if(nbr->sum_list == NULL) {
				make_lsdb_sum_list(id,rtif->rt_table,nbr->nbr_id);
      				ospf_send_ack(id,nbr,intf);
			    }
			    else {
				ospf_send_ack(id,nbr,intf);
			    }
	break;
	default : break;
    }

    intf->send_flag = send_flag;
}

/*

  ospf_message_recv

*/
void ospf_message_recv(rtif,type)
struct RT_IFCONF *rtif;
int type;
{
    struct PROTO_OSPF *ospf,*recv_ospf;
    struct OSPF_IF *intf,*recv_intf;
    u_long id;
    u_long src,dst;
    int recv_type;
    int send_flag;
    int recv_id;
    struct RT_IFCONF *recv_rtif;
    
      
    if(rtif->self->trace_flag) {
	log_trace(rtif->self->trace_fp,"\n <<< OSPF Message Recv >>>\n");
    }

    log_trace(stderr,"\n <<< OSPF Message Recv >>>\n");
    
    ospf = (struct PROTO_OSPF *)rtif->proto_info;
    intf = ospf->myarea->intf;
    id = rtif->self->router_id;

    send_flag = intf->send_flag;
    intf->send_flag = NO_SEND;
    while(1) {
	if(recv_packet(&src,&dst,pkt_buff)) {
	    if(dst == OSPF_ALLSPF_ADDR) {
		if(src == IF_ADDR(rtif->remote_addr) && intf->type == BROADCAST) {
		    recv_type = ospf_recv_packet(id,ospf,src,dst,pkt_buff);
		    if(type == recv_type) break;
		}
		else {
		    recv_rtif = get_local_rtif(src);
		    if(recv_rtif) { /* if multicast intrface exit */
			recv_ospf = (struct PROTO_OSPF *)recv_rtif->proto_info;
			(void)ospf_recv_packet(recv_rtif->self->router_id,recv_ospf,src,dst,pkt_buff);
		    }
		    else { 		/* multicast interface don't exit */
			(void)ospf_recv_packet(id,NULL,src,dst,pkt_buff);
		    }
		}
	    }
	    else if((recv_id = get_router_id(dst)) == id) {
		recv_type = ospf_recv_packet(id,ospf,src,dst,pkt_buff);
		if(type == recv_type) break;
	    }
	    else {
		recv_ospf = (struct PROTO_OSPF *)get_proto_info(recv_id,dst);
		(void)ospf_recv_packet(recv_id,recv_ospf,src,dst,pkt_buff);
	    }
	}
    }

    intf->send_flag = send_flag;
    
}

/*

  event_chang_dump

*/
void event_change_dump(fp,id,event,old_state,new_state)
FILE *fp;
u_long id;
int event;
int old_state;
int new_state;
{
    log_trace(fp,"\n	 Router ID : %s  Event %s \n",
	      inetaddr_to_char(htonl(id)),ospf_nbr_event[event]);
    log_trace(fp,"	                Neighbor State %s -> %s\n",
	    ospf_nbr_state[old_state],ospf_nbr_state[new_state]);
}

/*

  ospf_nbr_state_start

*/
void ospf_nbr_state_start(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_START,nbr->state,NBR_ATTEMPT);
	event_change_dump(stderr,id,EVT_START,nbr->state,NBR_ATTEMPT);
    }
    
    nbr->state = NBR_ATTEMPT;

    if(intf->priority && intf->send_flag)
      ospf_send_hello(id,nbr,intf);

}

/*

  ospf_nbr_state_hello

*/
void ospf_nbr_state_hello(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_HELLO_RECEIVED,nbr->state,NBR_INIT);
	event_change_dump(stderr,id,EVT_HELLO_RECEIVED,nbr->state,NBR_INIT);
    }
    
    nbr->state = NBR_INIT;

    if(intf->send_flag)
       timer_set(intf->htimer,intf->hello_timer);
    
}

/*

  ospf_nbr_state_2way

*/
void ospf_nbr_state_2way(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_2_WAY,nbr->state,NBR_2WAY);
	event_change_dump(stderr,id,EVT_2_WAY,nbr->state,NBR_2WAY);	
    }
    
    nbr->state = NBR_2WAY;

    if(intf->type == NONBROADCAST && intf->state > IFS_POINT_TO_POINT) {
	ospf_if_state_nbrchange(id,intf);
    }

    if((intf->type > NONBROADCAST && intf->type <= VIRTUAL_LINK)
    || (nbr->state == NBR_2WAY && intf->type < POINT_TO_POINT)
/*        && (intf->dr == ntohl(IF_ADDR(nbr->nbr_addr))
        || intf->bdr == ntohl(IF_ADDR(nbr->nbr_addr))))*/
    )
       ospf_nbr_state_adjok(id,intf,nbr);
       
}

/*

  ospf_nbr_state_adjok

*/
void ospf_nbr_state_adjok(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_ADJ_OK,nbr->state,NBR_EXSTART);
	event_change_dump(stderr,id,EVT_ADJ_OK,nbr->state,NBR_EXSTART);	
    }
    
    nbr->state = NBR_EXSTART;
    nbr->seq = get_currnt_time();
    
    nbr->bits = (INIT | MORE | MASTER_SLAVE);
    
    /*    if(intf->send_flag) {
	ospf_send_database(id,nbr,intf);
    }*/
}

/*

  ospf_nbr_state_mismactch

*/
void ospf_nbr_state_mismatch(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    struct LS_REQ *req,*nreq;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_SEQNUM_MISMATCH,nbr->state,NBR_EXSTART);
	event_change_dump(stderr,id,EVT_SEQNUM_MISMATCH,nbr->state,NBR_EXSTART);
    }

    
    nbr->state = NBR_EXSTART;

    nbr->sum_list = NULL;
    for(req=nbr->req_list;req;) {
	nreq=req->next;
	free((char *)req);
	req = nreq;
    }

    nbr->req_list = NULL;
    
    nbr->bits = (INIT | MORE | MASTER_SLAVE);
    nbr->seq = get_currnt_time();
    if(intf->send_flag) ospf_send_database(id,nbr,intf);
}


/*

  ospf_nbr_state_negodone

*/
void ospf_nbr_state_negodone(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_NEGO_DONE,nbr->state,NBR_EXCHANGE);
	event_change_dump(stderr,id,EVT_NEGO_DONE,nbr->state,NBR_EXCHANGE);
    }
    
    dbsum_list(id,nbr,intf);

    nbr->state = NBR_EXCHANGE;
}

/*

  ospf_nbr_state_exchange

*/
void ospf_nbr_state_exchange(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_EXCH_DONE,nbr->state,NBR_LOADING);
	event_change_dump(stderr,id,EVT_EXCH_DONE,nbr->state,NBR_LOADING);
    }
    
    if(nbr->mode != MASTER) {
	nbr->mode = SLAVE_HOLD;
    }

    nbr->state = NBR_LOADING;

    if(nbr->req_list == NULL)
       ospf_nbr_state_loading(id,intf,nbr);
}

/*

  ospf_nbr_state_loading

*/
void ospf_nbr_state_loading(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_LOAD_DONE,nbr->state,NBR_FULL);
	event_change_dump(stderr,id,EVT_LOAD_DONE,nbr->state,NBR_FULL);	
    }
    
    nbr->state = NBR_FULL;
}

/*

  ospf_nbr_state_badreq

*/
void ospf_nbr_state_badreq(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    struct LS_REQ *req,*nreq;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_BAD_LS_REQ,nbr->state,NBR_EXSTART);
	event_change_dump(stderr,id,EVT_BAD_LS_REQ,nbr->state,NBR_EXSTART);
    }

    nbr->state = NBR_EXSTART;

    nbr->sum_list = NULL;
    for(req=nbr->req_list;req;) {
	nreq=req->next;
	free((char *)req);
	req = nreq;
    }

    nbr->seq = get_currnt_time();
    nbr->bits = (INIT | MORE | MASTER_SLAVE);
    if(intf->send_flag) ospf_send_database(id,nbr,intf);
}


/*

  ospf_nbr_state_1way

*/
void ospf_nbr_state_1way(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    struct LS_REQ *req,*nreq;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_1_WAY,nbr->state,NBR_INIT);
	event_change_dump(stderr,id,EVT_1_WAY,nbr->state,NBR_INIT);
    }

    nbr->state = NBR_INIT;

    nbr->sum_list = NULL;
    for(req=nbr->req_list;req;) {
	nreq=req->next;
	free((char *)req);
	req = nreq;
    }

}

/*

  ospf_nbr_state_down

*/
void ospf_nbr_state_down(id,intf,nbr)
u_long id;
struct OSPF_IF *intf;
struct OSPF_NBR *nbr;
{
    FILE *fp;
    struct LS_REQ *req,*nreq;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	event_change_dump(fp,id,EVT_INACT_TIMER,nbr->state,NBR_DOWN);
	event_change_dump(stderr,id,EVT_INACT_TIMER,nbr->state,NBR_DOWN);
    }

    nbr->state = NBR_DOWN;

    intf->bdr = 0;
    nbr->bdr = 0;

    set_dr(id,nbr,intf);
}


/*

  if_event_change_dump

*/
void if_event_change_dump(fp,id,event,old_state,new_state)
FILE *fp;
u_long id;
int event;
int old_state;
int new_state;
{
    log_trace(fp,"\n	 Router ID : %s  Event %s \n",
	      inetaddr_to_char(htonl(id)),ospf_if_event[event]);
    log_trace(fp,"	                Interface State %s -> %s\n",
	    ospf_if_state[old_state],ospf_if_state[new_state]);
}

/*

  ospf_if_state_ifup

*/
void ospf_if_state_ifup(id,intf)
u_long id;
struct OSPF_IF *intf;
{
    FILE *fp;
    int old_state;
    
    old_state = intf->state;

    switch(intf->type) {
    case BROADCAST : intf->state = IFS_WAITING;
		     ospf_send_hello(id,intf->nbr,intf);
		     if(intf->send_flag)
			  timer_set(intf->htimer,intf->hello_timer);
			break;
    case VIRTUAL_LINK : intf->state = IFS_POINT_TO_POINT;
			ospf_send_hello(id,intf->nbr,intf);
			break;
    case NONBROADCAST : if(intf->priority) {
			    intf->state = IFS_WAITING;
			    if(intf->send_flag)
				timer_set(intf->htimer,intf->hello_timer);
			}
			else
			    intf->state = IFS_OTHER;
			ospf_nbr_state_start(id,intf,intf->nbr);
			break;
	default : break;
    }

    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	if_event_change_dump(fp,id,EVT_INTF_UP,old_state,intf->state);
	if_event_change_dump(stderr,id,EVT_INTF_UP,old_state,intf->state);
    }
    
}

/*

  ospf_if_state_nbrchange

*/
void ospf_if_state_nbrchange(id,intf)
u_long id;
struct OSPF_IF *intf;
{
    FILE *fp;
    int old_state;
    struct OSPF_NBR *nbr;
    u_long dr,bdr;
    
    old_state = intf->state;
    nbr = intf->nbr;

    dr = bdr = 0;
    if(intf->priority < nbr->priority) {
	dr = IF_ADDR(nbr->nbr_addr);
	if(intf->priority) {
	    bdr = IF_ADDR(intf->ifaddr);
	    intf->state = IFS_BDR;
	}
    }
    else if(intf->priority == nbr->priority) {
	if(ntohl(nbr->nbr_id) > ntohl(id)) {	/* nbr id DR */
	    dr = IF_ADDR(nbr->nbr_addr);
	    bdr = IF_ADDR(intf->ifaddr);
	    intf->state = IFS_BDR;
	}
	else {			/* I am DR */
	    dr = IF_ADDR(intf->ifaddr);
	    bdr = IF_ADDR(nbr->nbr_addr);
	    intf->state = IFS_DR;
	}
    }
    else {
	intf->state = IFS_DR;
	if(nbr->priority)
	  bdr = IF_ADDR(nbr->nbr_addr);
	else
	  return ;
    }

    if(dr && intf->dr != dr) {
	intf->dr = nbr->dr = dr;
	change_dr(IF_NET(intf->ifaddr),nbr->nbr_id,id);
	table_update(id,IF_ADDR(intf->ifaddr));
    }
    
    if(bdr)
	intf->bdr = nbr->bdr = bdr;
			  
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	if_event_change_dump(fp,id,EVT_NBR_CHANGE,old_state,intf->state);
	if_event_change_dump(stderr,id,EVT_NBR_CHANGE,old_state,intf->state);
    }

}

/*

  ospf_if_state_backupseen

*/
void ospf_if_state_backupseen(id,intf)
u_long id;
struct OSPF_IF *intf;
{
    FILE *fp;
    int old_state;

    old_state = intf->state;
    
    if(get_trace_flag(id)) {
	fp = get_trace_fp(id);
	if_event_change_dump(fp,id,EVT_BACKUP_SEEN,old_state,intf->state);
	if_event_change_dump(stderr,id,EVT_BACKUP_SEEN,old_state,intf->state);
    }

}


/*

  ospf_rt_table

*/
char *ospf_rt_table(id,rtif)
u_long id;
struct RT_IFCONF *rtif;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_AREA *area;
    struct OSPF_NBR *nbr;
    int type;
    int arg;
    char *rt_table;
    
    ospf = (struct PROTO_OSPF *)rtif->proto_info;
    arg = 0;
    rt_table = NULL;
    for(area=ospf->myarea;area;area=area->next) {
	type = area->type;
	if(type == AREA_TYPE_STUB) {
	    arg = area->default_cost;
	}
	else if(type == AREA_TYPE_VIRTUAL) {
	    arg = get_rtid_with_ifaddr(IF_ADDR(rtif->remote_addr));
	}
	area->lsdb = make_lsdb(id,RT_IF_ADDR(rtif),type,arg);
	if(!rt_table) rt_table = area->lsdb;
	nbr = area->intf->nbr;
/*	if(nbr->sum_list != NULL)
	  make_lsdb_sum_list(id,area->lsdb,nbr->nbr_id);*/
	  
    }

    return rt_table;
}

/*

  ospf_start

*/
void ospf_start(id,rtif)
u_long id;
struct RT_IFCONF *rtif;
{
    struct PROTO_OSPF *ospf,*recv_ospf;
    struct OSPF_AREA *area;
    int type;
    struct OSPF_IF *intf,*recv_intf;
    struct OSPF_NBR *nbr;
    u_long src,dst;
    byte *packet;
    struct RT_CONF *rt;
    u_long recv_id;
    struct RT_IFCONF *recv_rtif;
    
    ospf = (struct PROTO_OSPF *)rtif->proto_info;
    bzero(pkt_buff,1500);
    
    for(area=ospf->myarea;area;area=area->next) {
	intf = area->intf;
	if(intf->type == VIRTUAL_LINK) continue;
	
	nbr = intf->nbr;

	intf->send_flag = SEND_OK;
	set_dr(id,nbr,intf);
	ospf_if_state_ifup(id,intf);
	
	while(nbr->state < NBR_FULL) {
	    timer_check();
	    if(recv_packet(&src,&dst,pkt_buff)) {
		if(dst == OSPF_ALLSPF_ADDR || dst == OSPF_ALLDR_ADDR) {
		    if(src == IF_ADDR(rtif->remote_addr) && intf->type == BROADCAST) {
			type = ospf_recv_packet(id,ospf,src,dst,pkt_buff);
		    }
		    else {
			recv_rtif = get_local_rtif(src);
			if(recv_rtif && src != IF_ADDR(rtif->local_addr)) { /* if multicast intrface exit */
			    recv_ospf = (struct PROTO_OSPF *)recv_rtif->proto_info;
			    if(recv_rtif->state == INTF_UP)
			      (void)ospf_recv_packet(recv_rtif->self->router_id,recv_ospf,src,dst,pkt_buff);
			}
			else if (src != IF_ADDR(rtif->local_addr)) { /* multicast interface don't exit */
			    (void)ospf_recv_packet(id,NULL,src,dst,pkt_buff);
			}
		    }
		}
		else if( dst == IF_ADDR(rtif->local_addr)) {
		    type = ospf_recv_packet(id,ospf,src,dst,pkt_buff);
		}
		else {
		    recv_rtif = get_local_rtif(src);
		    if(recv_rtif) {
			recv_ospf = (struct PROTO_OSPF *)recv_rtif->proto_info;
			if(recv_rtif->state == INTF_UP)
			  type = ospf_recv_packet(recv_rtif->self->router_id,recv_ospf,src,dst,pkt_buff);
		    }
		    else {
			(void)ospf_recv_packet(id,NULL,src,dst,pkt_buff);
		    }
		}
		bzero(pkt_buff,1500);
	    }
	}    
    }

    time_count();
    ospf->refresh_time = get_currnt_time() + OSPF_LS_REFRESH;
    
    if(!ospf->vlink)
      return ;

    router_wait(id,10);
    for(area=ospf->myarea;area;area=area->next) {
	intf = area->intf;
	if(intf->type != VIRTUAL_LINK) continue;
	
	nbr = intf->nbr;

	intf->send_flag = SEND_OK;
	ospf_if_state_ifup(id,intf);

	while(nbr->state < NBR_FULL) {
	    if(recv_packet(&src,&dst,pkt_buff)) {
		if(dst == OSPF_ALLSPF_ADDR) { /* own is non-broadcast */
		    recv_rtif = get_local_rtif(src); /* don't come multicast packet for me */
		    if(recv_rtif) {
			recv_ospf = (struct PROTO_OSPF *)recv_rtif->proto_info;
			(void)ospf_recv_packet(recv_rtif->self->router_id,recv_ospf,src,dst,pkt_buff);
		    }
		    else {
			(void)ospf_recv_packet(id,NULL,src,dst,pkt_buff);
		    }
		}
		else if((recv_id = get_router_id(dst)) == id) {
		    type = ospf_recv_packet(id,ospf,src,dst,pkt_buff);
		}
		else {
		    recv_ospf = (struct PROTO_OSPF *)get_proto_info(recv_id,dst);
		    (void)ospf_recv_packet(recv_id,recv_ospf,src,dst,pkt_buff);
		}
	    }
	}
    }

}

/*

  ospf_if_up

*/
void ospf_if_up(id,proto_info)
u_long id;
byte *proto_info;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;
    
    ospf = (struct PROTO_OSPF *)proto_info;
    intf = ospf->myarea->intf;
    nbr = intf->nbr;

    intf->send_flag = SEND_OK;

    set_dr(id,nbr,intf);
    ospf_if_state_ifup(id,intf);
    
}

/*

  ospf_if_down

*/
void ospf_if_down(proto_info)
byte *proto_info;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;
    struct OSPF_AREA *area;

    ospf = (struct PROTO_OSPF *)proto_info;
    
    for(area=ospf->myarea;area;area=area->next) {
	intf = area->intf;
	nbr = intf->nbr;
    
	if(intf->htimer)
	  timer_stop(intf->htimer);

	if(intf->rtimer)
	  timer_stop(intf->rtimer);

	intf->send_flag = NO_SEND;

	nbr->state = NBR_DOWN;
	intf->state = IFS_DOWN;

	intf->dr = 0;
	intf->bdr = 0;
    }
}

/*

  ospf_recv_wait

*/
void ospf_recv_wait(id,rtif)
u_long id;
struct RT_IFCONF *rtif;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_IF *intf;
    short save_flag;
    struct OSPF_HDR *hdr;
    u_long src;
    u_long dst;
    byte *pkt;
    u_long recv_id;
    struct RT_IFCONF *recv_rtif;
    int t;

    if(!recv_packet(&src,&dst,pkt_buff))
      return;

    ospf = (struct PROTO_OSPF *)rtif->proto_info;
    intf = ospf->myarea->intf;

    time_count();
    if(ospf->refresh_time <= get_currnt_time()) {
	log_trace(stderr," flooding start \n");
	ospf_flood(id,rtif->proto_info);
	ospf->refresh_time = get_currnt_time() + OSPF_LS_REFRESH;
    }
    
    if(dst == OSPF_ALLSPF_ADDR || dst == OSPF_ALLDR_ADDR) {
	if(src == IF_ADDR(rtif->remote_addr) && intf->type == BROADCAST) {
	    if(intf->send_flag == SEND_OK) 
	      (void)ospf_recv_packet(id,ospf,src,dst,pkt_buff);
	    else
	      ospf_recv_packet(id,NULL,src,dst,pkt_buff);
	}
	else {
	    recv_rtif = get_local_rtif(src);
	    if(recv_rtif && src != IF_ADDR(rtif->local_addr)) { /* if multicast intrface exit */
		ospf = (struct PROTO_OSPF *)recv_rtif->proto_info;
		if(recv_rtif->state == INTF_UP)
		  (void)ospf_recv_packet(recv_rtif->self->router_id,ospf,src,dst,pkt_buff);
	    }
	    else { 		/* multicast interface don't exit */
		(void)ospf_recv_packet(id,NULL,src,dst,pkt_buff);
	    }
	}
	return ;
    }

    if(dst == IF_ADDR(rtif->local_addr)) {
	if(ospf && ospf->myarea->intf->send_flag == SEND_OK)
	  (void)ospf_recv_packet(id,ospf,src,dst,pkt_buff);
	else
	  (void)ospf_recv_packet(id,NULL,src,dst,pkt_buff);
    }
    else {
	recv_rtif = get_local_rtif(src);
	if(recv_rtif) {
	    ospf = (struct PROTO_OSPF *)recv_rtif->proto_info;
	    if(ospf && ospf->myarea->intf->send_flag == SEND_OK) 
	      ospf_recv_packet(recv_rtif->self->router_id,ospf,src,dst,pkt_buff);
	    else
	      ospf_recv_packet(recv_rtif->self->router_id,NULL,src,dst,pkt_buff);
	}
	else {
	    ospf_recv_packet(id,NULL,src,dst,pkt_buff);
	}
    }
/*
    if((recv_id = get_router_id(dst)) != id) {
	ospf = (struct PROTO_OSPF *)get_proto_info(recv_id,dst);
    }
    
    if(ospf) {
	intf = ospf->myarea->intf;
	if(intf->send_flag == SEND_OK) {
	    ospf_recv_packet(recv_id,ospf,src,dst,pkt_buff);
	}
	else {
	    ospf_recv_packet(recv_id,NULL,src,dst,pkt_buff);
	}
    }
    else {
	printf("ospf=NULL");
	ospf_recv_packet(recv_id,NULL,src,dst,pkt_buff);
    }
*/
    
}


/*

  hello_time_out

*/
void hello_time_out(id,data)
u_long id;
char *data;
{
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;
    time_t currnt_time;

    intf = (struct OSPF_IF *)data;
    nbr=intf->nbr;

    ospf_send_hello(id,nbr,intf);

    currnt_time = get_currnt_time();
    if(time_conv(currnt_time - nbr->recv_time) > intf->dead_timer
    && nbr->state > NBR_DOWN) {
	ospf_nbr_state_down(id,intf,nbr);
    }
}

/*

  retrans_time_out

*/
void retrans_time_out(id,data)
u_long id;
char *data;
{
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;

#ifdef DEBUG
    printf("retrans time out\n");
#endif
    printf("retrans time out\n");    
    intf = (struct OSPF_IF *)data;
    nbr=intf->nbr;

    if(nbr->state < NBR_EXSTART)
      return;

    if(nbr->state == NBR_EXSTART) {
	  ospf_send_database(id,nbr,intf);
	  return;
      }

#ifdef DEBUG
    printf("state=%d, mode=%d, sum_list=%d\n",nbr->state, nbr->mode, nbr->sum_list);
#endif

    if((nbr->state == NBR_EXCHANGE) && (nbr->mode == MASTER)    
    && (nbr->sum_list != NULL)) {
      ospf_send_database(id,nbr,intf);
    }
    
}


/*

  ospf_rt_update
  modefied  1996.7.10
            add argument 'flag' for send/nosend update pkt

*/
void ospf_rt_update(id,proto_info,flag)
u_long id;
byte *proto_info;
int flag;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;

    ospf = (struct PROTO_OSPF *)proto_info;
    intf = ospf->myarea->intf;
    nbr = intf->nbr;

    if(intf->area->lsdb) { /* already have updated LSDB */
      if(intf->area->adv_lsa) 
	free_lsdb_sum_list(intf->area->adv_lsa);
      intf->area->adv_lsa = make_lsdb_sum_list(id,intf->area->lsdb,nbr->nbr_id);
    }

    if(flag && intf->send_flag == SEND_OK)
      ospf_send_update(id,nbr,intf,ospf->myarea->adv_lsa);

}
    
/*

  ospf_rt_unreachable

*/
void ospf_rt_unreachable(id,proto_info,area_num,as_num)
u_long id;
char *proto_info;
u_long area_num;
int as_num;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;
    int send_flag;
    
    ospf = (struct PROTO_OSPF *)proto_info;
    intf = ospf->myarea->intf;
    nbr = intf->nbr;

    if(ospf->myarea->lsdb)	/* already interface up */
      send_flag = lsdb_unreachable(id,ospf->myarea->adv_lsa,area_num,as_num);
    
    if(send_flag && intf->send_flag == SEND_OK)
      ospf_send_update(id,nbr,intf,ospf->myarea->adv_lsa);
}

/*

  ospf_rt_aging

*/
void ospf_rt_aging(id,proto_info)
u_long id;
char *proto_info;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;
    int trans_timer;
    
    ospf = (struct PROTO_OSPF *)proto_info;
    intf = ospf->myarea->intf;
    nbr = intf->nbr;

    if(ospf->myarea->lsdb)	/* already interface up */
      lsdb_aging(id,ospf->myarea->lsdb);
    
    trans_timer = intf->trans_timer;
    intf->trans_timer = MAX_AGE;
    ospf_send_update(id,nbr,intf,ospf->myarea->adv_lsa);
    intf->trans_timer = trans_timer;
}

/*

  ospf_flood

*/
void ospf_flood(id,proto_info)
u_long id;
char *proto_info;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_IF *intf;
    struct OSPF_NBR *nbr;
    int trans_timer;
    
    ospf = (struct PROTO_OSPF *)proto_info;
    intf = ospf->myarea->intf;
    nbr = intf->nbr;

    if(ospf->myarea->adv_lsa)	/* already interface up */
      lsdb_flooding(id,ospf->myarea->adv_lsa);

    if(intf->send_flag == SEND_OK) {
	ospf_send_update(id,nbr,intf,ospf->myarea->adv_lsa);
    }
}

/*

  ospf_dump

*/
void ospf_dump(fp,proto_info)
FILE *fp;
byte *proto_info;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_AREA *area;

    ospf = (struct PROTO_OSPF *)proto_info;

    for(area=ospf->myarea;area;area=area->next) {
      lsa_dump(fp,area->adv_lsa);
	lsdb_dump(fp,area->lsdb);
    }
}

/*

  ospf_bin_dump

*/
void ospf_bin_dump(fp,proto_info)
FILE *fp;
byte *proto_info;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_AREA *area;

    ospf = (struct PROTO_OSPF *)proto_info;

    for(area=ospf->myarea;area;area=area->next) {
	lsdb_bin_dump(fp,area->lsdb);
    }
}

/*

  ospf_load

*/
void ospf_load(fp,id,rtif)
FILE *fp;
u_long id;
struct RT_IFCONF *rtif;
{
    struct PROTO_OSPF *ospf;
    struct OSPF_AREA *area;
    u_short as_num;
    int type,arg;
    
    ospf = (struct PROTO_OSPF *)rtif->proto_info;

    for(area=ospf->myarea;area;area=area->next) {
	type = area->type;
	if(type = AREA_TYPE_STUB) {
	    arg = area->default_cost;
	}
	else if(type = AREA_TYPE_VIRTUAL) {
	    arg = get_rtid_with_ifaddr(IF_ADDR(rtif->remote_addr));
	}
	area->lsdb = lsdb_load(fp,id,RT_IF_ADDR(rtif),type,arg);
    }
}    

/*

  ospf_display

*/
void  ospf_display(proto_info,type,flag)
byte *proto_info;
int type;
short flag;
{
    struct PROTO_OSPF *ospf;

    ospf = (struct PROTO_OSPF *)proto_info;

    switch(type) {
    case OSPF_HELLO_PKT : ospf->hello_display = flag;
		     break;
    case OSPF_DB_PKT : ospf->db_display = flag;
		       break;
    case OSPF_LS_REQ_PKT : ospf->req_display = flag;
			   break;
    case OSPF_LS_UPDATE_PKT : ospf->up_display = flag;
				break;
    case OSPF_LS_ACK_PKT : ospf->ack_display = flag;
	break;
	default : break;
    }
    
}

/*

  ospf_display_ok

*/
short ospf_display_ok(proto_info,type)
byte *proto_info;
{
    struct PROTO_OSPF *ospf;
    short flag;
    
    ospf = (struct PROTO_OSPF *)proto_info;

    switch(type) {
    case OSPF_HELLO_PKT : flag = ospf->hello_display ;
		     break;
    case OSPF_DB_PKT : flag = ospf->db_display ;
		       break;
    case OSPF_LS_REQ_PKT : flag = ospf->req_display ;
			   break;
    case OSPF_LS_UPDATE_PKT : flag = ospf->up_display ;
				break;
    case OSPF_LS_ACK_PKT : flag = ospf->ack_display ;
	break;
	default : break;
    }

    return flag;
}

	

    
