/* $Id: router.c,v 1.1 1999/08/23 16:18:40 naamato Exp $ */
/*
 * Copyright(c) 1998 by Hitachi.Ltd All rights reserved.
 *
 */

#include "defs.h"
#include "igmp_def.h"
#include "dvmrp_def.h"
#include "interface.h"

#define  ONE_WAY 2
#define  UP 1
#define  DOWN 0

#define PRUNE 1
#define GRAFT 2

#define DVMRP_PKT  (struct igmp *)(send_buf+IPHDR_LEN)

struct NODE_IF {
    struct NODE_IF *next;
    struct if_addr *ifa;
    u_long local_addr;
    u_long remote_addr;
    short  state;
    short  protocol;
    char *if_ps;
};
    
struct NODE {
    struct NODE *next;
    u_long node_id;
    struct NODE_IF ifaddr;
    FILE *trace_fp;
    char *trace_file;
};

#define NODE_ENABLE  0x01
#define NODE_DISABLE 0x02
#define NODE_SEND    0x40
#define NODE_RECV    0x80

struct ROUTE {
    struct ROUTE *next;
    u_long addr;
    u_long mask;
    u_long gateway;
    int metric;
    int state;
};

struct NET_LIST {
    struct NET_LIST *next;
    int id;
    struct ROUTE *routes;
};

struct dvmrp_nbr {
    struct dvmrp_nbr *next;
    u_long node_id;
    u_long addr;
    u_long gen_id;
    u_short vers;   /* Store minior & major version */
    char cap;
    char state;   /* nbr UP or DOWN ? */
    u_long recv_time;
    struct ROUTE *routes;
};

struct vif {
    u_long lcl_addr;
    u_long rmt_addr;
    u_long netmask;
    struct if_info *ifap;
    struct dvmrp_nbr *nbrs;
};

struct network_list {
    struct network_list *next;
    u_long addr;
    u_long mask;
    int metric;
};

struct prune_list {
    struct prune_list *next;
    u_long src;
    u_long grp;
    int lifetime;
    int state;        /* Prune or Graft */
    u_long nbr_addr;
};

struct member {
    struct member *next;
    u_long group;
};

struct dvmrp_data {
    struct NODE *node;
    u_long gen_id;
    int probe_time;
    char *ptimer;
    int repor_time;
    char *rtimer;
    int graft_time;
    char *gtimer;
    int nbr_time;
    char *ntimer;
    struct dvmrp_nbr *nbrs;
    int nbr_count;
    int vifs; 
    struct ROUTE *dvmrp_route[32];
    struct NET_LIST *nets;
    struct member *member_list;
    struct prune_list *prunes;
    u_short vers;
    char cap_flag;
    char state;
};

struct sg_entry {
    struct sg_entry *next;
    u_long src;
    u_long group;
    int iif;
};

#define NODE_SEARCH(node,id) \
{ \
    for(node=router_list;node!=NULL;node=node->next) { \
	if(node->node_id == id) break; \
    } \
    if(!node) { \
	printf(" WARNING! : No define router %d\n",id); \
	return; \
    }\
}

struct if_addr *interface_addr();
void dvmrp_recv();
void probe_timeout();
void report_timeout();
void graft_timeout();
void dvmrp_nbr_timeout();
void probe_recv_newnbr();
void report_recv_update();
void graft_recv();
void graft_ack_recv();
void sg_create_entry();
char *timer_init();
struct dvmrp_nbr *find_dvmrp_nbr();
void src_rt_update();
u_long src_nexthop();
struct ROUTE *route_search();

extern char *send_buf;
extern char *recv_buf;
extern time_t currnt_time;
extern struct vif *vifs[];
extern char *version[];
extern char *copyright[];

struct NODE *router_list=NULL;
struct GROUP *active_group=NULL;
struct ROUTE *src_net=NULL;     /* routing table from nbrs */
struct sg_entry *sg_table=NULL; /* forwarding cache table */
int comp_type=0;                /* compare dvmrp packet for "recv" command */
char *recv_timer=NULL;

/*
 *
 */
void router_init(file)
char *file;
{
    int rt;
    char log_file[256];

    if(file)
	strcpy(log_file,file);
    else
	strcpy(log_file,"dnbr.log");
#if 0
    if(!(rt=log_init(log_file))) {
	printf("log file(%s) can't open \n",log_file);
    }

    log(LOG_INFO,0," \n");
    log(LOG_INFO,0," %s\n",version);
    log(LOG_INFO,0," \n");
/*    log(LOG_INFO,0," %s\n\n",copyright);*/
#endif
    igmp_recv_register(IGMP_PROTO_DVMRP,dvmrp_recv);
    dvmrp_recv_register(DVMRP_PROBE,probe_recv_newnbr);
    dvmrp_recv_register(DVMRP_REPORT,report_recv_update);
    dvmrp_recv_register(DVMRP_GRAFT,graft_recv);
    dvmrp_recv_register(DVMRP_GRAFT_ACK,graft_ack_recv);
    sg_recv_register(sg_create_entry);
}

#define LIST_ADD(head, node, new)  { \
        if(!head) head = new; \
        else { \
            for(node=head;(node)->next!=NULL;node=(node)->next); \
            (node)->next = new; \
        } \
    }

/*
 *
 */
void router_add(id,addr,cap_flag,vers)
u_long id;
u_long addr;
char cap_flag;
u_short vers;
{
    struct NODE *new,*node;
    struct NODE_IF *nif,*new_nif;
    struct dvmrp_data *ps;
    struct if_infp *ifp;
    struct if_addr *ifap;
    int vif;
    u_long net;
    int i;

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_DEBUG,0," TRACE router add %d %s\n",id,inet_atos(htonl(addr)));

    if(!(ifap = interface_addr(addr))) {
	printf(" WARNING! : interface %s don't exist\n",inet_atos(htonl(addr)));
	return;
    }

    for(node=router_list;node!=NULL;node=node->next) {
	if(node->node_id == id) break;
    }

    if(!node) {
	new=(struct NODE *)malloc(sizeof(struct NODE));
	new->node_id = id;
	new_nif = &new->ifaddr;
	new->trace_fp = NULL;
	new->trace_file = NULL;

	LIST_ADD(router_list,node,new);
    }
    else {
	for(nif=&node->ifaddr;nif->next!=NULL;nif=nif->next) ;
	MALLOC(new_nif, NODE_IF);
	nif->next = new_nif;
    }

    new_nif->local_addr = addr;
    new_nif->remote_addr = 0;
    new_nif->ifa = ifap;
    new_nif->protocol = IPPROTO_IGMP;
    MALLOC(ps,dvmrp_data);
    new_nif->if_ps = (char *)ps;
    new_nif->state = UP;

    ps->node = new;
    ps->vers = vers;
    ps->cap_flag = (cap_flag ? cap_flag : DVMRP_CAP_GENID | DVMRP_CAP_PRUNE | DVMRP_CAP_MTRACE);
#if 0
    if(cap_flag) 
        ps->cap_flag = cap_flag;
    else
	ps->cap_flag = 
#endif

    ps->probe_time = DVMRP_PROBE_INTERVAL;
    ps->ptimer = timer_init(id,new_nif->if_ps,probe_timeout);
    ps->rtimer = timer_init(id,new_nif->if_ps,report_timeout);
    ps->gtimer = timer_init(id,new_nif->if_ps,graft_timeout);
    ps->ntimer = timer_init(id,new_nif->if_ps,dvmrp_nbr_timeout);

    if((vif = vif_withaddr(addr)) < 0)
	printf(" No vif %d %s\n",vif,inetaddr_to_char(htonl(addr)));
    else 
	ps->vifs = vif;

    ps->state = DOWN;
    ps->nbr_count = 0;

    /* add attached network */
    net = IF_ADDR(new_nif->ifa) & IF_NETMASK(new_nif->ifa);
    /*add_network_list2(&(ps->rt_table),net,IF_NETMASK(new_nif->ifa),1);*/
    for(i=0;i<32;i++)
	ps->dvmrp_route[i] = NULL;
/*    add_dvmrp_route(ps->dvmrp_route,net,IF_NETMASK(new_nif->ifa),1);*/

}

/*
 *
 */
void router_add_net(id,net_id)
u_long id;
u_long net_id;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct NET_LIST *net,*new_net;
    struct ROUTE *rt,*get_route_list();
    int len;

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_DEBUG,0," TRACE router add net %d %d\n",id,net_id);

    NODE_SEARCH(node,id);

    nif=&node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;
    MALLOC(new_net,NET_LIST);
    LIST_ADD(ps->nets,net,new_net);

    ps->gen_id = 0;

    new_net->id = net_id;
    for(rt=get_route_list(net_id);rt!=NULL;rt=rt->next) {
	add_dvmrp_route(ps->dvmrp_route,rt->addr,rt->mask,rt->metric);
    }
}

/*
 *  TODO
 */
void router_add_tunnel(id, rmt_addr)
u_long id;
u_long rmt_addr;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct NET_LIST *net,*new_net;
    struct ROUTE *rt,*get_route_list();
    int len;

    log(LOG_INFO,0," router add tunnel %d %s\n",id,inetaddr_to_char(htonl(rmt_addr)));

    for(node=router_list;node!=NULL;node=node->next) {
	if(node->node_id == id) break;
    }

    if(!node) {
	return;
    }

    nif=&node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;

    nif->remote_addr = rmt_addr;
    dvmrp_tunnel_add(nif->local_addr,rmt_addr);
}

#define ADD_NBR_LIST(list,nbr)  \
	for(nbr_list=list;nbr_list->next!=NULL;nbr_list=list->next); \
        nbr_list->next = nbr

/*
 *
 */		 
void router_up(id)
u_long id;
{
    struct NODE *node;
    struct NODE_IF *nif,*nif2;
    struct dvmrp_data *ps,*ps2;
    struct dvmrp_nbr *n,*n2,*nbr_list;
    int vif;
    int len;
    u_long dst;
#if 0
    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_DEBUG,0," router up %d \n",id);
#endif
    for(node=router_list;node!=NULL;node=node->next) {
	if(node->node_id == id) break;
    }

    if(!node) {
	printf(" WARNING! : No define router %d\n",id);
	return;
    }

    nif=&node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;
    timer_set(ps->ptimer,ps->probe_time);
    ps->state = UP;
    ps->gen_id = currnt_time;

    dst = (nif->remote_addr ? nif->remote_addr : DVMRP_GROUP);
    len = dvmrp_probe_send2(IF_NAME(nif->ifa),nif->local_addr,dst,ps->cap_flag,ps->gen_id,ps->vers,NULL);
	
    if(node->trace_fp) {
	trace(node->trace_fp," *** router up ***\n\n");
	dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(dst),DVMRP_PKT,len);
    }
    /* add I to other router */
    for(node=router_list;node!=NULL;node=node->next) {
	if(node->node_id == id) continue;

	nif2 = &(node->ifaddr);
	ps2 = (struct dvmrp_data *)nif2->if_ps;

	if(ps->vifs != ps2->vifs) 
	    continue;

	if(ps2->state == DOWN)
	    continue;
	log(LOG_INFO,0," nbr %d %s\n",node->node_id,inetaddr_to_char(htonl(nif->local_addr)));
	if(ps2->nbrs) {
	    n = find_dvmrp_nbr(ps2->nbrs,IF_ADDR(nif->ifa));
	    if(n) {
		/*   */
		printf(" found nbr \n");
	    }
	    else { /* Add New Neighbor */
		MALLOC(n, dvmrp_nbr);
		n->gen_id = ps->gen_id;
		n->addr = IF_ADDR(nif->ifa);
		n->cap = ps->cap_flag;
		n->vers = ps->vers;
		n->state = ps->state;
		n->recv_time = 0;

		log(LOG_INFO,0," Add New Nighbor(%s) to %d\n",inet_atos(htonl(n->addr)),node->node_id);
		ADD_NBR_LIST(ps2->nbrs,n);
		ps2->nbr_count++;
	    }
	}
	else {  /* Add First Neighbor */
	    log(LOG_INFO,0," Add First Nighbor \n");
	    MALLOC(n,dvmrp_nbr);
	    n->gen_id = ps->gen_id;
	    n->addr = IF_ADDR(nif->ifa);
	    n->cap = ps->cap_flag;
	    n->vers = ps->vers;
	    n->state = ps->state;
	    n->recv_time = 0;
	}

	MALLOC(n2, dvmrp_nbr);
	n2->gen_id = ps2->gen_id;
	n2->addr = IF_ADDR(nif2->ifa);
	n2->cap = ps2->cap_flag;
	n2->vers = ps2->vers;
	n2->state = ps2->state;
	n2->recv_time = 0;

	log(LOG_DEBUG,0," Add my neighbor list %s \n",
	    inet_atos(htonl(n2->addr)));

	if (ps->nbrs) { 
	    ADD_NBR_LIST(ps->nbrs,n2);
	}
        else {
	    ps->nbrs = n2;
	    /* If first neighbor , start nbr_timeout timer */
	    timer_set(ps->ntimer, 5);
	}
	ps->nbr_count++;

    } /* router_list */

}

/*
 *
 */
void router_down(id)
u_long id;
{
    struct NODE *node;
    struct NODE_IF *nif,*nif2;
    struct dvmrp_data *ps,*ps2;
    struct dvmrp_nbr *n,*nbr_list,*prev;
    int vif;
    int len;
    struct ROUTE *rtp;

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," TRACE router down %d \n",id);

    NODE_SEARCH(node,id);

    nif=&node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;
    timer_stop(ps->ptimer);
    timer_stop(ps->rtimer);
    ps->state = DOWN;

    for(rtp=src_net;rtp!=NULL;rtp=rtp->next) {
        del_dvmrp_route(ps->dvmrp_route,rtp->addr,rtp->mask,rtp->metric+32);
    }

    if(node->trace_fp)
	trace(node->trace_fp," *** router down ***\n\n");

    /* delete I from other router */
    for(node=router_list;node!=NULL;node=node->next) {
	if(node->node_id == id) continue;

	nif2 = &(node->ifaddr);
	ps2 = (struct dvmrp_data *)nif2->if_ps;

	if(ps->vifs != ps2->vifs) 
	    continue;

	if(ps2->state == DOWN)
	    continue;

	if(ps2->nbrs) {
	    prev = NULL;
	    for(nbr_list=ps2->nbrs;nbr_list;nbr_list=nbr_list->next) {
		if(nbr_list->addr == nif->local_addr) {
		    if(prev) prev->next = nbr_list->next;
		    else {
			if(nbr_list->next)
			    ps2->nbrs = nbr_list->next;
			else
			    ps2->nbrs = NULL;
		    }
		    free(nbr_list);
		    break;
		}
		prev = nbr_list;
	    } /* for */
	    ps2->nbr_count--;
	}

    } /* router_list */

    /* Delete all nbr from out list */
    if(ps->nbrs) {  
	for(nbr_list=ps->nbrs;nbr_list!=NULL;) {
	    nbr_list = nbr_list->next;
	    if(nbr_list) free(nbr_list);
	}
	ps->nbr_count = 0;
	ps->nbrs = NULL;
    }

}

/*
 *
 */
void router_enable(id)
u_long id;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct dvmrp_nbr *n,*nbr_list,*prev;
    int vif;
    int len;
    struct ROUTE *rtp;

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," TRACE router enable %d \n",id);

    NODE_SEARCH(node,id);

    nif=&node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;

    if(node->trace_fp)
	trace(node->trace_fp," *** router enable ***\n\n");

}

/*
 *
 */
void router_disable(id)
u_long id;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct dvmrp_nbr *n,*nbr_list,*prev;
    int vif;
    int len;
    struct ROUTE *rtp;

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," TRACE router disable %d \n",id);

    NODE_SEARCH(node,id);

    nif=&node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;

    if(node->trace_fp)
	trace(node->trace_fp," *** router disable ***\n\n");

}

/*
 *
 */
void router_update_route(net_id,addr,mask,metric) 
u_long net_id;
u_long addr,mask;
int metric;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct NET_LIST *net;
    struct ROUTE *rt,*update_route[32],*get_route_list();
    int len,i;

    for(i=0;i<32;i++) update_route[i]=NULL;
    for(node=router_list;node!=NULL;node=node->next) {
	nif = &(node->ifaddr);
	ps = (struct dvmrp_data *)nif->if_ps;
	for(net=ps->nets;net!=NULL;net=net->next) {
	    if(net->id != net_id)
		continue;

	    for(rt=get_route_list(net_id);rt!=NULL;rt=rt->next) {
	        if(addr == rt->addr && mask == rt->mask) break;
	    }
	    if(rt) { /* found update route */
	        update_dvmrp_route(ps->dvmrp_route,rt->addr,rt->mask,rt->metric);
		add_dvmrp_route(update_route,rt->addr,rt->mask,rt->metric);
	    }
	    else { /* not found, add new route */
	        add_dvmrp_route(ps->dvmrp_route,addr,mask,metric);
	        add_dvmrp_route(update_route,addr,mask,metric);
	    }

	    len = dvmrp_report_send2(IF_NAME(nif->ifa),nif->local_addr,DVMRP_GROUP,ps->vers,update_route);
	    if(node->trace_fp)
	        dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(DVMRP_GROUP),DVMRP_PKT,len);
	}
    }
}

/*
 *
 */
void probe_timeout(id,data)
u_long id;
char *data;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    int len;
    u_long dst;

    if(IS_DEBUG(DEBUG_TIMER))
	log(LOG_DEBUG,0," TIMER probe_timeout %d \n",id);    

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)data;
    if(nif->remote_addr) dst = nif->remote_addr;
    else dst = DVMRP_GROUP;

    len = dvmrp_probe_send2(IF_NAME(nif->ifa),nif->local_addr,dst,ps->cap_flag,ps->gen_id,ps->vers,ps->nbrs);

    if(node->trace_fp)
	dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(dst),DVMRP_PKT,len);
	
}

/*
 *
 */
void report_timeout(id,data)
u_long id;
char *data;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    int len;

    if(IS_DEBUG(DEBUG_TIMER))
	log(LOG_DEBUG,0," TIMER report_timeout %d \n",id);    

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)data;
    
    if(!ps->nets)
      return;

    len = dvmrp_report_send2(IF_NAME(nif->ifa),nif->local_addr,DVMRP_GROUP,ps->vers,ps->dvmrp_route);

    if(node->trace_fp)
	dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(DVMRP_GROUP),
		   DVMRP_PKT,len);

}

/*
 *
 */
void graft_timeout(id,data)
u_long id;
char *data;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct prune_list *prn;
    int len;

    if(IS_DEBUG(DEBUG_TIMER))
	log(LOG_DEBUG,0," TIMER graft_timeout %d \n",id);    

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)data;
    
    if(!ps->nets)
      return;

    for(prn=ps->prunes;prn!=NULL;prn=prn->next) {
	if(prn->state == GRAFT) {
	    dvmrp_graft_send(IF_NAME(nif->ifa),nif->local_addr,prn->nbr_addr,ps->vers,prn->src,prn->grp);
	    dvmrp_trace(NULL,htonl(nif->local_addr),htonl(DVMRP_GROUP),
		   (struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+12);

	    if(node->trace_fp)
		dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(DVMRP_GROUP),
		   (struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+12);
	}
    }
}

/*
 *
 */
void dvmrp_nbr_timeout(id,data)
u_long id;
char *data;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct dvmrp_nbr *nbr;
    int len;
    struct ROUTE *src,*r;

    if(IS_DEBUG(DEBUG_TIMER))
	log(LOG_DEBUG,0," TIMER nbr_timeout %d \n",id);    

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)data;
    for(nbr=ps->nbrs;nbr;nbr=nbr->next) {
        if(nbr->state == DOWN) continue;
        if(nbr->recv_time && currnt_time - nbr->recv_time > DVMRP_NBR_TIMEOUT) {
	    log(LOG_DEBUG,0," Neighbor %s Down \n",inet_atos(htonl(nbr->addr)));
	    nbr->state = DOWN;
	    for(src=src_net;src;src=src->next) {
	        if(src->gateway == nbr->addr) {
		    r = route_search(nbr->routes,src->addr,src->mask,src->metric);
		    r->metric = DVMRP_INFINITY;
		    src->metric = DVMRP_INFINITY;
		    
		    update_dvmrp_route(ps->dvmrp_route,src->addr,src->mask,DVMRP_INFINITY);
		}
	    }
	    ps->nbr_count--;
	    if(!ps->nbr_count) {
		timer_stop(ps->rtimer);
	    }
	}

    }

}
    
/*
 *
 */
void recv_timeout(id,data)
u_long id;
char *data;
{
    timer_stop(recv_timer);
    if(comp_type) {
	printf("   Timeout Receive Packet \n");
	log(LOG_INFO,0,"     Timeout Receive Packet \n");
	comp_type = 0;
    }
}

/*
 *
 */
void check_dvmrp_packet(src,dst,type,packet,pkt_len)
u_long src,dst;
int type;
char *packet;
int pkt_len;
{
    if(type == comp_type) {
	dvmrp_comp_register(NULL);
	comp_type = 0;
	wait_timeout(0,NULL);
	printf(" Packet Receive \n");
	log(LOG_INFO,0," Packet Receive \n");
    }
    else {
	printf(" Packet Receive \n");
	log(LOG_INFO,0," Packet Receive \n");
    }
}

/*
 *
 */
void comp_packet_recv(type,timeout)
int type;
int timeout;
{
    comp_type = type;
    dvmrp_comp_register(check_dvmrp_packet);
    if(!recv_timer)
	recv_timer = timer_init(0,NULL,recv_timeout);
    timer_set(recv_timer,timeout);
}

/*
 *
 */
int member_search(list, group)
struct member *list;
u_long group;
{
    struct member *mem;

    for(mem=list;mem!=NULL;mem=mem->next) {
	if(mem->group == group)
	    return 1;
    }

    return 0;
}

/*
 *
 */
u_long source_locate(addr)
u_long addr;
{
    struct ROUTE *rt;

    for(rt=src_net;rt!=NULL;rt=rt->next) {
	if(rt->addr == (addr & rt->mask)) 
	    return rt->gateway;
    }
    return 0;
}

/*
 *
 */	
void prune_process(id,src,dst)
u_long id;
u_long src,dst;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct prune_list *prn,*new_prn;
    long gateway;
    int iif;
    
    log(LOG_INFO,0," prune_process %d %s\n",id,inet_atos(htonl(dst)));

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;

    new_prn = (struct prune_list *)malloc(sizeof(struct prune_list));
    new_prn->src = src;
    new_prn->grp = dst;
    new_prn->state = PRUNE;
    if(gateway = source_locate(src)) {
	new_prn->nbr_addr = gateway;
    }
    else {
	/* src is on direct network */
	iif = vif_withaddr(src);
	if(iif >= 0)
	    new_prn->nbr_addr = src;
    }
    new_prn->lifetime = DVMRP_DEFAULT_LIFETIME;

    if(ps->prunes) {
	for(prn=ps->prunes;prn->next!=NULL;prn=prn->next);
	prn->next = new_prn;
    }
    else
	ps->prunes = new_prn;

    dvmrp_prune_send(IF_NAME(nif->ifa),nif->local_addr,ps->vers,new_prn->nbr_addr,src,dst,DVMRP_DEFAULT_LIFETIME);
    dvmrp_trace(NULL,htonl(nif->local_addr),htonl(new_prn->nbr_addr),
		    (struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+16);
    if(node->trace_fp)
        dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(new_prn->nbr_addr),
		    (struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+16);

}

/*
 *
 */
void graft_process(id,dst)
u_long id;
u_long dst;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct prune_list *prn;

    log(LOG_INFO,0," graft_process %d %s\n",id,inet_atos(htonl(dst)));

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;
    for(prn=ps->prunes;prn!=NULL;prn=prn->next) {
	if(prn->grp == dst) {
	    prn->state = GRAFT;
	    dvmrp_graft_send(IF_NAME(nif->ifa),nif->local_addr,prn->nbr_addr,ps->vers,prn->src,dst);
	    dvmrp_trace(NULL,htonl(nif->local_addr),htonl(prn->nbr_addr),
		    (struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+12);
	    if(node->trace_fp)
	        dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(prn->nbr_addr),
		    (struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+12);

	    return;
	}
    }

}

/*
 *
 */
void sg_create_entry(src,dst)
u_long src,dst;
{
    int iif;
    struct sg_entry *new_sg,*sgp;
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    u_long pre_hop;

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," sg_create_entry (%s,%s)\n",
	    inet_ipstr(htonl(src),ipstr1),inet_ipstr(htonl(dst),ipstr2));

    pre_hop = src_nexthop(src);
    if(pre_hop) 
	iif = vif_withaddr(pre_hop);
    else
	/* Check src is on direct network */
	iif = vif_withaddr(src);

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," sg previous_hop %s iif %d\n",
	    inet_atos(htonl(pre_hop)),iif);

    if(iif<0) 
        return;

    new_sg = (struct sg_entry *)malloc(sizeof(struct sg_entry));
    if(sg_table) {
	for(sgp=sg_table;sgp->next!=NULL;sgp=sgp->next);
	sgp->next = new_sg;
    }
    else {
	sg_table = new_sg;
    }
    new_sg->src = src;
    new_sg->group = dst;
    new_sg->iif = iif;

    kernel_add_entry(htonl(src),htonl(dst),iif);

#if 0  /* experimental function , not use yet */
    for(node=router_list;node!=NULL;node=node->next) {
	nif = &node->ifaddr;
	ps = (struct dvmrp_data *)nif->if_ps;
	if(!member_search(ps->member_list,dst))
	    prune_process(node->node_id,src,dst);
    }
#endif
}

/*
 *
 */
void probe_recv_newnbr(vif,src,dst,nbr,igmp,igmp_len)
int vif;
u_long src,dst;
struct dvmrp_nbr *nbr;
struct igmp *igmp;
int igmp_len;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    u_long *dvmrp,rt;
    struct dvmrp_nbr *nbr_list,*n;
    int nbr_change;
    int len,rlen;

    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," TRACE probe_recv_newnbr %s\n",inet_atos(htonl(nbr->addr)));

    for(node=router_list;node!=NULL;node=node->next) {

	nbr_change=0;
	nif = &(node->ifaddr);
	ps = (struct dvmrp_data *)nif->if_ps;

	if(vif != ps->vifs) 
	    continue;

	if(ps->state == DOWN)
	    continue;

	if(node->trace_fp) 
	    dvmrp_trace(node->trace_fp,htonl(src),htonl(dst),igmp,igmp_len);

	if(ps->nbrs) {
	    n = find_dvmrp_nbr(ps->nbrs,nbr->addr);
	    if(n) {
		if(n->gen_id != nbr->gen_id) nbr_change++;
		else if(n->state == UP) nbr_change++;
		else if(n->state == DOWN) {
		    nbr_change++;
		    ps->nbr_count++;
		}
		if(n->cap != nbr->cap) n->cap = nbr->cap; /* 1999.8.5 */
		n->recv_time = currnt_time;
	    }
	    else { /* Add New Neighbor */
		if(IS_DEBUG(DEBUG_TRACE))
		    log(LOG_INFO,0," TRACE Add new nbr to %d\n",node->node_id);
		n = (struct dvmrp_nbr *)malloc(sizeof(struct dvmrp_nbr));
		bzero(n,sizeof(struct dvmrp_nbr));
		n->gen_id = nbr->gen_id;
		n->addr = nbr->addr;
		n->cap = nbr->cap;
		n->vers = nbr->vers;
		n->state = nbr->state = UP;
		n->recv_time = currnt_time;
		nbr_change++;
		for(nbr_list=ps->nbrs;nbr_list->next;nbr_list=nbr_list->next);
		nbr_list->next = n;
		ps->nbr_count++;
	    }
	}
	else {  /* Add First Neighbor */
	    if(IS_DEBUG(DEBUG_TRACE))
		log(LOG_INFO,0," TRACE Add First nbr %d\n",node->node_id);
	    n = ps->nbrs = (struct dvmrp_nbr *)malloc(sizeof(struct dvmrp_nbr));
	    bzero(n,sizeof(struct dvmrp_nbr));
	    n->gen_id = nbr->gen_id;
	    n->addr = nbr->addr;
	    n->cap = nbr->cap;
	    n->vers = nbr->vers;
	    n->state = nbr->state = UP;
	    n->recv_time = currnt_time;
	    nbr_change++;
	    ps->nbr_count++;
	    timer_set(ps->ntimer, 5);
	}

	if(nbr_change) {
	    if(IS_DEBUG(DEBUG_TRACE))
		log(LOG_INFO,0," TRACE Neighbor Change %s\n",inet_atos(htonl(n->addr)));
	    len = igmp_len - sizeof(u_long) - sizeof(struct igmp);
	    dvmrp = (u_long *)(igmp+1);
	    dvmrp++;  /* skip gen_id */
	    for(;len;len-=sizeof(u_long)) {
		rt = ntohl(*dvmrp++);
		if(rt == nif->local_addr) {
		    /* If there is net-list , send Report */
		    if(ps->nets) {
		        rlen=dvmrp_report_send2(IF_NAME(nif->ifa),nif->local_addr,DVMRP_GROUP,ps->vers,ps->dvmrp_route);
		    /* dvmrp_trace(NULL,htonl(nif->local_addr),htonl(DVMRP_GROUP),
				(struct igmp *)(send_buf+IPHDR_LEN),rlen);*/
			if(node->trace_fp)
			    dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(DVMRP_GROUP),
				(struct igmp *)(send_buf+IPHDR_LEN),rlen);
			timer_set(ps->rtimer,DVMRP_REPORT_INTERVAL);
		    }
		    n->state = ONE_WAY;
		}
	    }
	}
    } /* router_list */
}

/*
 *
 */
void report_recv_update(vif,src,dst,nbr,igmp,igmp_len)
int vif;
u_long src,dst;
struct dvmrp_nbr *nbr;
struct igmp *igmp;
int igmp_len;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    char *dvmrp;
    struct dvmrp_nbr *nbr_list,*n;
    int len,rlen;
    u_long net,mask;
    int metric,masklen;
    struct ROUTE *rt,*new_rt,*last,*route_search();
    int octet,i;
    int update;

    if(nbr) {
	if(IS_DEBUG(DEBUG_TRACE))
	    log(LOG_INFO,0," TRACE report_recv_update %s\n",inet_atos(htonl(nbr->addr)));
    }
    else {
	if(IS_DEBUG(DEBUG_TRACE))
	    log(LOG_INFO,0," TRACE report recv unknow nbr(%s) from %d -- ignore \n",
		inet_atos(htonl(src)),vif);
	return;
    }
    
    for(node=router_list;node!=NULL;node=node->next) {

	nif = &(node->ifaddr);
	ps = (struct dvmrp_data *)nif->if_ps;
	update = 0;

	if(vif != ps->vifs) 
	    continue;

	if(ps->state == DOWN)
	    continue;

	if(ps->nbrs) {
	    n = find_dvmrp_nbr(ps->nbrs,nbr->addr);
	    if(!n) {
		printf(" No neighbor \n");
		continue;
	    }
	}
	else {
	    printf(" No neighbor \n");
	    continue;
	}

	if(node->trace_fp)
	    dvmrp_trace(node->trace_fp,htonl(src),htonl(dst),igmp,igmp_len);

	dvmrp = (u_char *)(igmp+1);
	len = igmp_len - sizeof(struct igmp);
	octet=0;
	for(;len>0;) {
	    mask=0;
	    ((char *)&mask)[0] = 0xff;
	    octet=1;
	    ((char *)&mask)[1] = *dvmrp++;
	    ((char *)&mask)[2] = *dvmrp++;
	    ((char *)&mask)[3] = *dvmrp++;
	    len -= 3;
	    for(i=1;i<4;i++) { 
		if(((char *)&mask)[i] !=0) octet++;
	    }
	    mask = ntohl(mask);
	    do {
		net=0;
		for(i=0;i<octet;i++)
		    ((char *)&net)[i] = *dvmrp++;
		net = ntohl(net);
		len -= octet;
		metric = *dvmrp++;
		metric &= 0x7f;
		len--;

		if(metric > 32) {
		    /* Poison reverse route. This route is our */
		    /* advetizing route. Nneighbor is dependent router */
		    /*  */
		}
		else if(metric == 32) {
		    /* */
		}
		else if(!(rt=route_search(n->routes,net,mask,metric))) {
		    /* If this route is new from neighbor, */
		    /* Add neighbor route list */
		    /*new_rt = (struct ROUTE *)malloc(sizeof(struct ROUTE));*/
		    MALLOC(new_rt,ROUTE);
		    new_rt->addr = net;
		    new_rt->mask = mask;
		    new_rt->metric = metric;
		    if(n->routes) {
			for(rt=n->routes;rt->next!=NULL;rt=rt->next);
			rt->next = new_rt;
		    }
		    else {
			n->routes = new_rt;
		    }

		    /* source table update */
		    src_rt_update(new_rt,n->addr);

		    /* Check if I hold same route. */
		    masklen = mask_bits(net,mask);
		    if(!route_search(ps->dvmrp_route[masklen],net,mask,metric)) {
			/* add poison reverse route to our list */
		        if(IS_DEBUG(DEBUG_TRACE)) 
		            log(LOG_DEBUG,0," TRACE New route %s(%d)\n",inet_atos(htonl(net)),metric);
			add_dvmrp_route(ps->dvmrp_route,net,mask,metric+32);
		    }
		    else {
		        if(IS_DEBUG(DEBUG_TRACE))
			    log(LOG_DEBUG,0," TRACE found route %s(%d)\n",inet_atos(htonl(net)),metric);
		    }
		    update=1;
		}
		else {
		    /* I hold already this route, update the route */
		    /* TODO: check if metric change */ 
		    if(rt->metric > metric) {
		        if(IS_DEBUG(DEBUG_TRACE))
			    log(LOG_DEBUG,0," TRACE More feasible route \n");
			rt->metric = metric;
			src_rt_update(rt,n->addr);
			update_dvmrp_route(ps->dvmrp_route,net,mask,metric+32);
		    }
		    else 
		        if(IS_DEBUG(DEBUG_TRACE))
			    log(LOG_DEBUG,0," TRACE Same route \n");
		}
	    } while(!(*(dvmrp-1) & 0x80));
	} /* for(;len>0;) */

	if(update) {
	    rlen=dvmrp_report_send2(IF_NAME(nif->ifa),nif->local_addr,DVMRP_GROUP,ps->vers,ps->dvmrp_route);

	    if(node->trace_fp)
	        dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(DVMRP_GROUP),DVMRP_PKT,rlen);
	}

    } /* router_list */

	
}

/*
 *
 */
void graft_recv(vif,src,dst,nbr,igmp,igmp_len)
int vif;
u_long src,dst;
struct dvmrp_nbr *nbr;
struct igmp *igmp;
int igmp_len;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    u_long *dvmrp,rt;
    int len,rlen;
    u_long sg_src,sg_grp;

    log(LOG_INFO,0," graft_recv %s\n",inetaddr_to_char(htonl(src)));

    dvmrp = (u_long *)(igmp+1);
    sg_src = ntohl(*dvmrp++);
    sg_grp = ntohl(*dvmrp);
    for(node=router_list;node!=NULL;node=node->next) {
	nif = &(node->ifaddr);
	if(dst != nif->local_addr)
	    continue;

	if(node->trace_fp) 
	    dvmrp_trace(node->trace_fp,htonl(src),htonl(dst),igmp,igmp_len);

	ps = (struct dvmrp_data *)nif->if_ps;
	if(ps->state == DOWN)
	    continue;
	
	dvmrp_gack_send(IF_NAME(nif->ifa),nif->local_addr,src,ps->vers,sg_src,sg_grp);
	if(node->trace_fp) 
	    dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(src),
			DVMRP_PKT,sizeof(struct igmp)+8);

    } /* router_list */
}

/*
 *
 */
void graft_ack_recv(vif,src,dst,nbr,igmp,igmp_len)
int vif;
u_long src,dst;
struct dvmrp_nbr *nbr;
struct igmp *igmp;
int igmp_len;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    u_long *dvmrp,rt;
    int len,rlen;
    u_long sg_src,sg_grp;

    log(LOG_INFO,0," graft_ack_recv %s\n",inet_atos(htonl(src)));

    dvmrp = (u_long *)(igmp+1);
    sg_src = ntohl(*dvmrp++);
    sg_grp = ntohl(*dvmrp);
    for(node=router_list;node!=NULL;node=node->next) {
	nif = &(node->ifaddr);
	if(dst != nif->local_addr)
	    continue;

	if(node->trace_fp) 
	    dvmrp_trace(node->trace_fp,htonl(src),htonl(dst),igmp,igmp_len);

	ps = (struct dvmrp_data *)nif->if_ps;
	if(ps->state == DOWN)
	    continue;
/*	
	dvmrp_gack_send(IF_NAME(nif->ifa),nif->local_addr,src,sg_src,sg_grp);
*/
	if(node->trace_fp) 
	    dvmrp_trace(node->trace_fp,htonl(nif->local_addr),htonl(src),
			(struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+8);
	dvmrp_trace(NULL,htonl(nif->local_addr),htonl(src),
			(struct igmp *)(send_buf+IPHDR_LEN),sizeof(struct igmp)+8);

    } /* router_list */
}

/*
 *
 */
void member_add(id,grp)
u_long id,grp;
{
    struct member *mem,*new_mem;
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct prune_list *prn;

    log(LOG_INFO,0," member_add %d %s\n",id,inet_atos(htonl(grp)));

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;
    for(mem=ps->member_list;mem!=NULL;mem=mem->next) {
      if(mem->group == grp) {
	printf(" Already Have member \n");
	    return;
      }
    }
    
    /* Add member per router */
    new_mem = (struct member *)malloc(sizeof(struct member));
    new_mem->group = grp;
    if(ps->member_list) {
	for(mem=ps->member_list;mem->next!=NULL;mem=mem->next);
	if(mem) mem->next = new_mem;
    }
    else 
	ps->member_list = new_mem;

    /* If send prune before, send graft */
    for(prn=ps->prunes;prn!=NULL;prn=prn->next) {
	if(prn->grp == grp) {
	    graft_process(id,grp);
	    return;
	}
    }
    printf(" No Prune State \n");
}

/*
 *
 */
void member_delete(id,grp)
u_long id,grp;
{
    struct member *mem,*prev;
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct sg_entry *sgp;

    log(LOG_INFO,0," member_delete %d %s\n",id,inet_atos(htonl(grp)));

    NODE_SEARCH(node,id);

    nif = &node->ifaddr;
    ps = (struct dvmrp_data *)nif->if_ps;
    prev = NULL;
    for(mem=ps->member_list;mem!=NULL;mem=mem->next) {
	if(mem->group == grp)
	    break;
	prev = mem;
    }
    if(prev) {
	prev->next = mem->next;
    }
    else if(mem==ps->member_list) {
        ps->member_list = NULL;
    }
    free(mem);

    for(sgp=sg_table;sgp!=NULL;sgp=sgp->next) {
	if(sgp->group == grp)
	    prune_process(id,sgp->src,grp);
    }
}

/*
 *
 */
void src_rt_update(rt,nexthop)
struct ROUTE *rt;
u_long nexthop;
{
    struct ROUTE *new,*rtp,*pre_rtp;
 
    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," TRACE src_rt_update %s\n",inet_atos(htonl(rt->addr)));

    if(src_net) {
	for(rtp=src_net;rtp!=NULL;rtp=rtp->next) {
	    if(rtp->addr == rt->addr && rtp->mask == rt->mask) {
	        if(rtp->metric != rt->metric) {
		    if(IS_DEBUG(DEBUG_TRACE)) 
		        log(LOG_INFO,0," TRACE route exist , but metric change\n %d -> %d \n",rtp->metric,rt->metric);
		    rtp->metric = rt->metric;
		}
		if(rtp->gateway != nexthop) {
		    if(IS_DEBUG(DEBUG_TRACE)) 
		        log(LOG_INFO,0," TRACE route  exist , but nexthop change \n");
		    rtp->gateway = nexthop;
		    return;
		}
		if(IS_DEBUG(DEBUG_TRACE))
		        log(LOG_INFO,0," TRACE route(%s) exist \n",inet_atos(htonl(rt->addr)));
		return;
	    }
	    pre_rtp = rtp;
	}
	MALLOC(new,ROUTE);
	pre_rtp->next = new;
    }
    else {
        MALLOC(new,ROUTE);
	src_net = new;
    }
    new->addr = rt->addr;
    new->mask = rt->mask;
    new->metric = rt->metric;
    new->gateway = nexthop;
}

/*
 *
 */
u_long src_nexthop(addr)
u_long addr;
{
    struct ROUTE *rtp;
    int iif;

    /* Search source network from neighbor */
    for(rtp=src_net;rtp!=NULL;rtp=rtp->next) {
      if(rtp->addr == (addr & rtp->mask)) {
	return rtp->gateway;
      }
    }

    return 0;
}

/*
 *
 */
void router_trace(id,file)
u_long id;
char *file;
{
    struct NODE *node;

    for(node=router_list;node!=NULL;node=node->next) {
	if(node->node_id == id) break;
    }
    if(!node) {
	printf(" No Router \n");
	return;
    }

    if((node->trace_fp=fopen(file,"w+")) == NULL) {
	printf(" %s can't open \n",file);
    }
    trace(node->trace_fp,"\n %s\n\n",version);
/*    trace(node->trace_fp," %s\n\n",copyright);*/

}
    
/*
 *
 */
void router_dump(id,file)
u_long id;
char *file;
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct network_list *net;
    struct NET_LIST *list;
    struct ROUTE *np;
    struct dvmrp_nbr *nbr;
    FILE *fp;
    int len;
    char *state[]={"Down","Up","Up"}; /* Second "Up" mean ONE_WAY state */
    struct prune_list *prn;

    if(file) {
	if((fp = fopen(file,"w+")) == NULL) {
	    trace(stderr,"can't open file(%s) \n",file);
	    fp = NULL;
	}
    }
    else {
	fp = NULL;
    }

    NODE_SEARCH(node,id);

    trace(fp,"\n Router %d \n",node->node_id);
    nif = &node->ifaddr;
    trace(fp," \t Addr: %s(%s) ", 
	       inet_atos(htonl(IF_ADDR(nif->ifa))),IF_NAME(nif->ifa));
    ps = (struct dvmrp_data *)nif->if_ps;
    trace(fp," \t vif: %d ",ps->vifs);
    trace(fp,"    state: %s \n",state[ps->state]);
    trace(fp," \t Ver: %d.%d \t GenID: %x ", ps->vers & 0x00ff,ps->vers>>8 & 0x00ff,ps->gen_id);
    trace(fp,"    Flags: ");
    if(ps->cap_flag & DVMRP_CAP_LEAF) trace(fp,"Leaf ");
    if(ps->cap_flag & DVMRP_CAP_PRUNE) trace(fp,"Prune ");
    if(ps->cap_flag & DVMRP_CAP_GENID) trace(fp,"Genid ");
    if(ps->cap_flag & DVMRP_CAP_MTRACE) trace(fp,"Mtrace ");
    if(ps->cap_flag & DVMRP_CAP_SNMP) trace(fp,"Snmp ");
    trace(fp,"\n");

/*    for(net=ps->rt_table;net;net=net->next) {
	trace(fp," \t Routes:%s",inet_atos(htonl(net->addr)));
	trace(fp,"   Mask:%s  Metric:%d \n",inet_atos(htonl(net->mask)),net->metric);
    }
*/
    trace(fp," \t Routes: \n");
    for(len=0;len<32;len++) {
        for(np=ps->dvmrp_route[len];np;np=np->next) {
	    trace(fp," \t   %-15s ",inet_atos(htonl(np->addr)));
	    trace(fp,"    mask: %-15s   metric: %d\n",inet_atos(htonl(np->mask)),np->metric);
	}
    }

    trace(fp," \t Net List: ");
    for(list=ps->nets;list;list=list->next) {
	trace(fp,"%d ",list->id);
    }

    trace(fp,"\n\t Neighbors<%d>:\n",ps->nbr_count);
    for(nbr=ps->nbrs;nbr;nbr=nbr->next) {
	trace(fp," \t   %s (%s)  GenID:%x   Flags:",inet_atos(htonl(nbr->addr)),state[nbr->state],nbr->gen_id);
	if(nbr->cap & DVMRP_CAP_LEAF) trace(fp,"L");
	if(nbr->cap & DVMRP_CAP_PRUNE) trace(fp,"P");
	if(nbr->cap & DVMRP_CAP_GENID) trace(fp,"G");
	if(nbr->cap & DVMRP_CAP_MTRACE) trace(fp,"M");
	if(nbr->cap & DVMRP_CAP_SNMP) trace(fp,"S");

	trace(fp,"  ver: %d", nbr->vers & 0x00ff);
	trace(fp,".%d \n", nbr->vers>>8 & 0x00ff);
	for(np=nbr->routes;np!=NULL;np=np->next) {
	    trace(fp," \t     %-15s ",inet_atos(htonl(np->addr)));
	    trace(fp,"    mask: %-15s   metric: %d\n",inet_atos(htonl(np->mask)),np->metric);
	}
    }

    trace(fp,"\t Prunes:\n");
    for(prn=ps->prunes;prn;prn=prn->next) {
        trace(fp," \t    (%s ,",inet_atos(htonl(prn->src)));
	trace(fp," %s)  %ds", inet_atos(htonl(prn->grp)),prn->lifetime);
	trace(fp," upstream %s \n",inet_atos(htonl(prn->nbr_addr)));
    }
    trace(fp,"\n");
}

void sg_entry_dump()
{
    struct sg_entry *sgp;

    printf("\n (S,G) Entrys: \n");
    if(!sg_table)
	printf(" \t none \n");
    for(sgp=sg_table;sgp!=NULL;sgp=sgp->next) {
	printf(" \t src %s ",inet_atos(htonl(sgp->src)));
	printf("  grp %s ",inet_atos(htonl(sgp->group)));
	printf("  iif %d \n",sgp->iif);
    }
    printf("\n");
}

/*
 *
 */
void src_net_dump()
{
    struct ROUTE *rt;

    printf("\n Source Network \n");
    for(rt=src_net;rt!=NULL;rt=rt->next) {
	printf("\t %s/ ",inet_atos(htonl(rt->addr)));
	printf("%s ",inet_atos(htonl(rt->mask)));
	printf(" Previous Hop: %s ",inet_atos(htonl(rt->gateway)));
	printf(" Metric: %d\n",rt->metric);
    }
    printf("\n");
}

/*
 *
 */
void dump()
{
    struct NODE *node;
    struct NODE_IF *nif;
    struct dvmrp_data *ps;
    struct network_list *net;
    struct dvmrp_nbr *nbr;

    for(node=router_list;node!=NULL;node=node->next) {
        router_dump(node->node_id,NULL);
    }

    vif_dump();
    network_dump();
    sg_entry_dump();
    src_net_dump();
}

/*
 *
 */
void do_report_send(if_name, src,dst,id)
char *if_name;
u_long src,dst;
u_long id; /* network-list id */
{
    struct ROUTE *rt, *get_route_list();

    if(!(rt=get_route_list(id))) {
	printf(" not found network-list %d \n",id);
	return;
    }

    for(;rt!=NULL;rt=rt->next) {
	add_network_list2(NULL,rt->addr,rt->mask,rt->metric);
    }
    dvmrp_report_send(if_name,src,dst);
    del_network_list();
}
