/* $Id: dvmrp.c,v 1.1 1999/08/23 16:18:30 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 MAXVIFS 32
#define BUF_SIZE  1024

#define NBR 0

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

struct dvmrp_prune {
    u_long src;
    u_long grp;
    u_long life_time;
};

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

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

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

struct _dvmrp_recv_func {
    void (*recv_routine)();
    int dummy;
};

#define MASKLEN(mask)   \
    { \
        bits = mask; \
        mask_len = 0; \
        while(0x80000000 & bits) { \
            mask_len++; \
	    bits <<= 1; \
        }\
    }

static struct _dvmrp_recv_func dvmrp_recv_func[DVMRP_MAX_TYPE];
void (*dvmrp_comp_func)();

void dvmrp_trace();
struct if_info *if_withaddr();
char *inet_atos();

u_long gen_id;
int vif_num=0;

char *send_buf;
char *recv_buf;

int dvmrp_flag;  /* Note!! experiment */

struct network_list *dvmrp_net[32];
extern struct if_info *if_list;
extern int trace_mode;
struct vif *vifs[MAXVIFS];

/*
 *
 */
void dvmrp_init()
{
    struct timeval boot_time;
    int bool;
    struct vif *vif;
    struct if_info *ifp;
    int i;

    igmp_init();
    
    gettimeofday(&boot_time,(struct timezone *)0);
    gen_id = boot_time.tv_sec;
    dvmrp_flag = 1;  /* DVMRP packet send/recv enable */

    send_buf = malloc(BUF_SIZE);
    bzero(send_buf,BUF_SIZE);

    for(ifp=if_list;ifp;ifp=ifp->next) {
	vif = (struct vif *)malloc(sizeof(struct vif));
	vif->lcl_addr = IF_ADDR(ifp->ipaddr);
	vif->netmask = IF_NETMASK(ifp->ipaddr);
	vif->ifap = ifp;
	vif->nbrs = NULL;
	vifs[vif_num++] = vif;
    }

    for(i=0;i<DVMRP_MAX_TYPE;i++)
	dvmrp_recv_func[i].recv_routine = NULL;
    dvmrp_comp_func = NULL;
}

/*
 *
 */
void dvmrp_tunnel_add(lcl_addr, rmt_addr)
u_long lcl_addr;
u_long rmt_addr;
{
    struct vif *vif;

    vif = (struct vif *)malloc(sizeof(struct vif));
    vif->lcl_addr = lcl_addr;
    vif->rmt_addr = rmt_addr;
    vif->ifap = NULL;
    vif->nbrs = NULL;
    vifs[vif_num] = vif;

    multicast_tunnel_vif(vif_num,lcl_addr,rmt_addr);
}

/*
 *
 */
void dvmrp_recv_register(type, callback)
int type;
void callback();
{
    dvmrp_recv_func[type].recv_routine = callback;
}

/*
 *
 */
void dvmrp_comp_register(callback)
void callback();
{
    dvmrp_comp_func = callback;
}

/*
 *
 */
void add_nbr_list2(if_name,nbr)
char *if_name;
u_long nbr;
{
    struct dvmrp_nbr *np,*nplist;
    struct vif *vifp;
    int i;

    for(i=0;i<vif_num;i++) {
	vifp = vifs[i];
	if(if_name) {
	    if(!strcmp(if_name, IF_NAME(vifp->ifap->ipaddr)))
		break;
	}
    }
    if(!if_name) vifp=vifs[0];
    if(i==vif_num) vifp=vifs[0];

    MALLOC(np,dvmrp_nbr);
    if(vifp->nbrs) {
        for(nplist=vifp->nbrs;nplist->next;nplist=nplist->next);
	np->addr = nbr;
	nplist->next = np;
    }
    else {
        np->addr = nbr;
        vifp->nbrs = np;
    }
    np->state = UP;
  
}

/*
 *
 */
void del_nbr_list2(if_name)
char *if_name;
{
    struct dvmrp_nbr *np,*nplist;
    struct vif *vifp;
    int i;

    for(i=0;i<vif_num;i++) {
	vifp = vifs[i];
	if(if_name) {
	    if(!strcmp(if_name, IF_NAME(vifp->ifap->ipaddr)))
		break;
	}
    }
    if(!if_name) vifp=vifs[0];
    if(i==vif_num) vifp=vifs[0];

    if(vifp->nbrs) {
        for(nplist=vifp->nbrs;np!=NULL;nplist=np) {
	    np = nplist->next;
	    free(nplist);
	}
	vifp->nbrs = NULL;
    }
}

/*
 *
 */
void add_vif_nbr_list(vif,nbr)
int vif;
struct dvmrp_nbr *nbr;
{
    struct dvmrp_nbr *nplist;
    struct vif *vifp;

    vifp = vifs[vif];
    if(vifp->nbrs) {
        for(nplist=vifp->nbrs;nplist->next;nplist=nplist->next);
	nplist->next = nbr;
    }
    else {
        vifp->nbrs = nbr;
    }
  
}

/*
 *
 */
void add_network_list2(list,net,mask,metric)
struct network_list **list;
u_long net;
u_long mask;
int metric;
{
    int mask_len;
    u_long bits;
    struct network_list *np,*nplist;

    MASKLEN(mask);

    MALLOC(np,network_list);
    np->addr = net;
    np->mask = mask;
    np->metric = metric;

    if(!list) {
        if(nplist = dvmrp_net[mask_len]) {
	    for(;nplist->next;nplist=nplist->next) ;
	    nplist->next = np;
	}
	else {
	  dvmrp_net[mask_len] = np;
	}
    }
    else if(list) {
        if(*list) {
	    nplist = *list;
	    for(;nplist->next;nplist=nplist->next) ;
	    nplist->next = np;
	}
	else {
	    *list = np;
	}
    }
}

/*
 *
 */
void del_network_list()
{
    int mask_len;
    u_long bits;
    struct network_list *np,*nplist;
    int i;

    for(i=0;i<32;i++) {
	if(nplist = dvmrp_net[i]) {
	    for(;np!=NULL;nplist=np){
		np  = nplist->next;
		free(nplist);
	    }
	    dvmrp_net[i] = NULL;
	}
    }
}

/*
 *
 */
void add_dvmrp_route(list,net,mask,metric)
struct ROUTE *list[];
u_long net;
u_long mask;
int metric;
{
    int mask_len;
    u_long bits;
    struct ROUTE *np,*nplist;

    bits = mask;
    mask_len = 0;
    while(0x80000000 & bits) {
        mask_len++;
	bits <<= 1;
    }

    MALLOC(np,ROUTE);
    np->addr = net;
    np->mask = mask;
    np->metric = metric;

    if(list[mask_len]) {
	nplist = list[mask_len];
        for(;nplist->next;nplist=nplist->next) ;
	nplist->next = np;
    }
    else {
        list[mask_len] = np;
    }
}

/*
 *
 */
void del_dvmrp_route(list,net,mask,metric)
struct ROUTE *list[];
u_long net,mask;
int metric;
{
    int mask_len;
    u_long bits;
    struct ROUTE *np,*nplist,*prev;

    bits = mask;
    mask_len = 0;
    while(0x80000000 & bits) {
        mask_len++;
	bits <<= 1;
    }

    for(nplist=list[mask_len];nplist!=NULL;nplist=nplist->next) {
      if(nplist->addr == net && nplist->mask == mask && nplist->metric == metric) {
	  if(nplist == list[mask_len]) {
	      list[mask_len] = nplist->next;
	  }
	  else {
	      prev->next = nplist->next;
	  }

	  free(nplist);
	  break;
      }
      prev = nplist;
    }
}

/*
 *
 */
void update_dvmrp_route(list,net,mask,metric)
struct ROUTE *list[];
u_long net;
u_long mask;
int metric;
{
    int mask_len;
    u_long bits;
    struct ROUTE *np,*nplist;

    bits = mask;
    mask_len = 0;
    while(0x80000000 & bits) {
        mask_len++;
	bits <<= 1;
    }

    for(np=list[mask_len];np!=NULL;np=np->next) {
	if(np->addr == net && np->mask == mask) {
	    np->metric = metric;
	    return;
	}
    }

    if(!np)
        add_dvmrp_route(list,net,mask,metric);

}

/*
 *
 */
void build_net_list(net,mask,num)
u_long net;
u_long mask;
int num;
{
    u_long start_net;
    struct in_addr iaddr,imask;
    int mask_len,host_len,i;
    u_long mask_bit,bits;

    MASKLEN(mask);
    host_len = 32 - mask_len;
    mask_bit = 0x00000001 << host_len;
    imask.s_addr = htonl(mask);

    start_net = net;
    for(i=0;i<num;i++) {
        if((start_net & 0xff000000) == 0xff000000
	|| (start_net & 0x00ff0000) == 0x00ff0000
	|| (start_net & 0x0000ff00) == 0x0000ff00) {
	    start_net += mask_bit;
	    continue;
	}
        iaddr.s_addr = htonl(start_net);
	add_network_list2(NULL,start_net, mask, 1);
        start_net += mask_bit;
    }

}

/*
 *
 */
void print_dvmrp_net()
{
    struct network_list *np;
    int len;
    
    for(len=0;len<32;len++) {
        for(np=dvmrp_net[len];np;np=np->next) {
	    printf(" net %s/%d %d\n",inet_ntoa(htonl(np->addr)),len,np->metric);
	}
    }
}

/*
 *
 */
void print_dvmrp_net2(dvmrp_route)
struct ROUTE *dvmrp_route[];
{
    struct ROUTE *np;
    int len;
    
    for(len=0;len<32;len++) {
        for(np=dvmrp_route[len];np;np=np->next) {
	    printf(" net %s/%d %d\n",inet_ntoa(htonl(np->addr)),len,np->metric);
	}
    }
}

/*
 *
 */
int dvmrp_probe_send2(if_name,src, grp, flag, id, ver,nbr)
char *if_name;
u_long src;
u_long grp;
char flag;
u_long id;
u_short ver;
struct dvmrp_nbr *nbr;
{
    struct ip *ip;
    struct igmp *igmp;
    char *dvmrp;
    u_long cap=DVMRP_CAP_GENID | DVMRP_CAP_PRUNE | DVMRP_CAP_MTRACE;
    int rtn;
    struct dvmrp_nbr *nbrp;
    u_long ifaddr,ip_src,ip_dst;
    int len;
    u_long nbr_addr;
    struct vif *vifp;
    int i;
    u_long gid;  /* Generation ID */

    bzero(send_buf,sizeof(send_buf));
    if(if_name) {
        ip = (struct ip *)send_buf;
	igmp = (struct igmp *)(ip+1);
	for(i=0;i<vif_num;i++) {
	    vifp = vifs[0];
	    if(!strcmp(if_name, IF_NAME(vifp->ifap->ipaddr)))
		break;
	}
	if(i == vif_num) vifp=vifs[0];
    }
    else {
        igmp = (struct igmp *)send_buf;
	vifp=vifs[0];
    }
    if(!vifp) {
	printf(" No Vif \n");
	return ;
    }

    dvmrp = (char *)(igmp+1);

    if(flag) cap = (u_long)flag;
    if(id) gid = id;
    else gid = gen_id;

    igmp->igmp_type = IGMP_PROTO_DVMRP;
    igmp->igmp_code = DVMRP_PROBE;
    if(ver)
	igmp->igmp_group = htonl((cap << 16) | ver);
    else
	igmp->igmp_group = htonl((cap << 16) | DVMRP_DEFAULT_VERSION);

    gid = htonl(gid);
    bcopy((char *)&gid, dvmrp, sizeof(u_long));
    dvmrp += sizeof(u_long);
    len = sizeof(u_long);
    if(nbr) nbrp = nbr;  
    else nbrp = vifp->nbrs;

    for(;nbrp;nbrp=nbrp->next) {
        if(src == nbrp->addr) 
	    continue;

	if(nbrp && nbrp->state == 0)  /* nbr state is DOWN */
	    continue;

        nbr_addr = htonl(nbrp->addr);
        bcopy((char *)&nbr_addr,dvmrp,sizeof(u_long));
	dvmrp += sizeof(u_long);
	len += sizeof(u_long);
    }

    igmp->igmp_cksum = 0;
    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+len);

    ip_dst = (grp ? htonl(grp) : htonl(DVMRP_GROUP));
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	ip_src = (src ? htonl(src) : ifaddr);
	if(dvmrp_flag) 
	    rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
	if(trace_mode)
	    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
    }
    else {
        if(dvmrp_flag)
	    igmp_send2(igmp,ip_dst,sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
    }


    return sizeof(struct igmp)+len;
}

/*
 *
 */
void dvmrp_report_send(if_name,src, grp)
char *if_name;
u_long src;
u_long grp;
{
    struct ip *ip;
    struct igmp *igmp;
    char *dvmrp;
    int rtn;
    struct network_list *np;
    u_long ifaddr;
    u_long ip_src,ip_dst;
    int len,i,octets,l;
    u_long net,mask;

    if(if_name) {
        ip = (struct ip *)send_buf;
	igmp = (struct igmp *)(ip+1);
    }
    else {
        igmp = (struct igmp *)send_buf;
    }
    dvmrp = (char *)(igmp+1);

    len = 0;
    igmp->igmp_type = IGMP_PROTO_DVMRP;
    igmp->igmp_code = DVMRP_REPORT;
    igmp->igmp_group = htonl((DVMRP_VERSION_MINOR << 8) | DVMRP_VERSION_MAJOR);

    for(i=0;i<32;i++) {

        if(i>=0 && i<=8) octets = 1;
	else if(i>8 && i<=16) octets = 2;
	else if(i>16 && i<=24) octets = 3;
	else if(i>24 && i<=32) octets = 4;

#if 1
	if(len + octets > MAX_DVMRP_DATA_LEN) {

	    *(dvmrp-1) |= 0x80;

	    igmp->igmp_cksum = 0;
	    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+len);

	    ip_dst = (grp ? htonl(grp) : htonl(DVMRP_GROUP));
	    if(if_name) {
		ifaddr = htonl(get_ifaddr_byname(if_name));
		ip_src = (src ? htonl(src) : ifaddr);
		rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
		if(trace_mode)
		    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
	    }
	    else {
		igmp_send2(igmp,ip_dst,sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
	    }
	    len = 0;
	    dvmrp = (char *)(igmp+1);
	}
#endif

	if(np=dvmrp_net[i]) {
	    mask = htonl(np->mask);
	    if(len) *(dvmrp-1) |= 0x80;
	    *dvmrp++ = ((char *)&mask)[1];
	    *dvmrp++ = ((char *)&mask)[2];
	    *dvmrp++ = ((char *)&mask)[3];
	    len += 3;

	    for(;np;np=np->next) {
#if 1
		if(len + octets > MAX_DVMRP_DATA_LEN) {
		    *(dvmrp-1) |= 0x80;

		    igmp->igmp_cksum = 0;
		    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+len);

		    ip_dst = (grp ? htonl(grp) : htonl(DVMRP_GROUP));
		    if(if_name) {
			ifaddr = htonl(get_ifaddr_byname(if_name));
			ip_src = (src ? htonl(src) : ifaddr);
			rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
			if(trace_mode)
			    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
		    }
		    else {
			igmp_send2(igmp,ip_dst,sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
		    }
		    len = 0;
		    dvmrp = (char *)(igmp+1);
		    mask = htonl(np->mask);
		    *dvmrp++ = ((char *)&mask)[1];
		    *dvmrp++ = ((char *)&mask)[2];
		    *dvmrp++ = ((char *)&mask)[3];
		    len += 3;

		}
#endif

		net = htonl(np->addr);
	    
		for(l=0;l<octets;l++) *dvmrp++ = ((char *)&net)[l];
		len += octets;

		*dvmrp++ = (char)np->metric;
		len++;
	    }
	}
    }

    if(len) *(dvmrp-1) |= 0x80;

    igmp->igmp_cksum = 0;
    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+len);

    ip_dst = (grp ? htonl(grp) : htonl(DVMRP_GROUP));
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	ip_src = (src ? htonl(src) : ifaddr); 
	if(dvmrp_flag)
	    rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
	if(trace_mode)
	    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
    }
    else {
        if(dvmrp_flag)
	    igmp_send2(igmp,ip_dst,sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
    }

}

/*
 *
 */
int dvmrp_report_send2(if_name,src,grp,ver,dvmrp_route)
char *if_name;
u_long src;
u_long grp;
u_short ver;
struct ROUTE *dvmrp_route[];
{
    struct ip *ip;
    struct igmp *igmp;
    char *dvmrp;
    int rtn;
    struct ROUTE *np;
    u_long ifaddr,ip_src,ip_dst;
    int len,i,octets,l;
    u_long net,mask;

    if(if_name) {
        ip = (struct ip *)send_buf;
	igmp = (struct igmp *)(ip+1);
    }
    else {
        igmp = (struct igmp *)send_buf;
    }
    dvmrp = (char *)(igmp+1);

    len = 0;
    igmp->igmp_type = IGMP_PROTO_DVMRP;
    igmp->igmp_code = DVMRP_REPORT;
    igmp->igmp_group = (ver ? htonl(ver) : htonl(DVMRP_DEFAULT_VERSION));

    for(i=0;i<32;i++) {

        if(i>=0 && i<=8) octets = 1;
	else if(i>8 && i<=16) octets = 2;
	else if(i>16 && i<=24) octets = 3;
	else if(i>24 && i<32) octets = 4;
#if 1
	if(len + octets > MAX_DVMRP_DATA_LEN) {

	    *(dvmrp-1) |= 0x80;

	    igmp->igmp_cksum = 0;
	    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+len);

	    ip_dst = (grp ? htonl(grp) : htonl(DVMRP_GROUP));
	    if(if_name) {
		ifaddr = htonl(get_ifaddr_byname(if_name));
		ip_src = (src ? htonl(src) : ifaddr);
		if(dvmrp_flag)
		    rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
		if(trace_mode)
		    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
	    }
	    else {
	        if(dvmrp_flag)
		    igmp_send2(igmp,ip_dst,sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
	    }
	    len = 0;
	    dvmrp = (char *)(igmp+1);
	}
#endif

	if(np=dvmrp_route[i]) {
	    mask = htonl(np->mask);
	pack:
	    if(len) *(dvmrp-1) |= 0x80;
	    *dvmrp++ = ((char *)&mask)[1];
	    *dvmrp++ = ((char *)&mask)[2];
	    *dvmrp++ = ((char *)&mask)[3];
	    len += 3;

	    for(;np;np=np->next) {
#if 1
		if(len > MAX_DVMRP_DATA_LEN) {
		    *(dvmrp-1) |= 0x80;

		    igmp->igmp_cksum = 0;
		    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+len);

		    ip_dst = (grp ? htonl(grp) : htonl(DVMRP_GROUP));
		    if(if_name) {
			ifaddr = htonl(get_ifaddr_byname(if_name));
			ip_src = (src ? htonl(src) : ifaddr);
			if(dvmrp_flag)
			    rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
			if(trace_mode)
			    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
		    }
		    else {
		        if(dvmrp_flag)
			    igmp_send2(igmp,ip_dst,sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
		    }
		    dvmrp = (char *)(igmp+1);
		    len = 0;
		    goto pack;
		}
#endif
		net = htonl(np->addr);
	    
		for(l=0;l<octets;l++) *dvmrp++ = ((char *)&net)[l];
		len += octets;

		*dvmrp++ = (char)np->metric;
		len++;

	    }
	}
    }

    if(len) *(dvmrp-1) |= 0x80;

    igmp->igmp_cksum = 0;
    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+len);

    ip_dst = (grp ? htonl(grp) : htonl(DVMRP_GROUP));
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	ip_src = (src ? htonl(src) : ifaddr);
	if(dvmrp_flag)
	    rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
	if(trace_mode)
	    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
    }
    else {
        if(dvmrp_flag)
	    igmp_send2(igmp,ip_dst,sizeof(struct igmp)+len,IGMP_TTL,ROUTER_ALERT_OFF);
    }

    return sizeof(struct igmp)+len;
}

/*
 *
 */
void dvmrp_prune_send(if_name,src,dst,ver,psrc,pgrp,life_time)
char *if_name;
u_long src,dst;
u_short ver;
u_long psrc,pgrp;
int life_time;
{
    struct ip *ip;
    struct igmp *igmp;
    struct dvmrp_prune *dvmrp;
    int rtn;
    u_long ifaddr,ip_src,ip_dst;
    int len;

    if(if_name) {
        ip = (struct ip *)send_buf;
	igmp = (struct igmp *)(ip+1);
    }
    else {
        igmp = (struct igmp *)send_buf;
    }
    dvmrp = (struct dvmrp_prune *)(igmp+1);

    igmp->igmp_type = IGMP_PROTO_DVMRP;
    igmp->igmp_code = DVMRP_PRUNE;
    igmp->igmp_group = (ver ? htonl(ver) : htonl(DVMRP_DEFAULT_VERSION));

    dvmrp->src = htonl(psrc);
    dvmrp->grp = htonl(pgrp);
    dvmrp->life_time = htonl(life_time);

    igmp->igmp_cksum = 0;
    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+sizeof(struct dvmrp_prune));

    ip_dst = htonl(dst);
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	ip_src = (src ? htonl(src) : ifaddr);
	rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+sizeof(struct dvmrp_prune),IGMP_TTL,ROUTER_ALERT_OFF);
	if(trace_mode)
	    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
    }
    else {
        igmp_send2(igmp,ip_dst,sizeof(struct igmp)+sizeof(struct dvmrp_prune),IGMP_TTL,ROUTER_ALERT_OFF);
    }

}

/*
 *
 */
void dvmrp_graft_send(if_name,src,dst,ver,psrc,pgrp)
char *if_name;
u_long src,dst;
u_short ver;
u_long psrc,pgrp;
{
    struct ip *ip;
    struct igmp *igmp;
    struct dvmrp_prune *dvmrp;
    int rtn;
    u_long ifaddr,ip_src,ip_dst;
    int len;

    if(if_name) {
        ip = (struct ip *)send_buf;
	igmp = (struct igmp *)(ip+1);
    }
    else {
        igmp = (struct igmp *)send_buf;
    }
    dvmrp = (struct dvmrp_prune *)(igmp+1);

    igmp->igmp_type = IGMP_PROTO_DVMRP;
    igmp->igmp_code = DVMRP_GRAFT;
    igmp->igmp_group = (ver ? htonl(ver) : htonl(DVMRP_DEFAULT_VERSION));

    dvmrp->src = htonl(psrc);
    dvmrp->grp = htonl(pgrp);

    igmp->igmp_cksum = 0;
    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+8);

    ip_dst = htonl(dst);
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	ip_src = (src ? htonl(src) : ifaddr );
	rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+8,IGMP_TTL,ROUTER_ALERT_OFF);
	if(trace_mode)
	    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
    }
    else {
        igmp_send2(igmp,ip_dst,sizeof(struct igmp)+8,IGMP_TTL,ROUTER_ALERT_OFF);
    }

}

/*
 *
 */
void dvmrp_gack_send(if_name,src,dst,ver,psrc,pgrp)
char *if_name;
u_long src,dst;
u_short ver;
u_long psrc,pgrp;
{
    struct ip *ip;
    struct igmp *igmp;
    struct dvmrp_prune *dvmrp;
    int rtn;
    u_long ifaddr,ip_src,ip_dst;
    int len;

    if(if_name) {
        ip = (struct ip *)send_buf;
	igmp = (struct igmp *)(ip+1);
    }
    else {
        igmp = (struct igmp *)send_buf;
    }
    dvmrp = (struct dvmrp_prune *)(igmp+1);

    igmp->igmp_type = IGMP_PROTO_DVMRP;
    igmp->igmp_code = DVMRP_GRAFT_ACK;
    igmp->igmp_group = (ver ? htonl(ver) : htonl(DVMRP_DEFAULT_VERSION));

    dvmrp->src = htonl(psrc);
    dvmrp->grp = htonl(pgrp);

    igmp->igmp_cksum = 0;
    igmp->igmp_cksum = in_cksum((u_short *)igmp, sizeof(struct igmp)+8);

    ip_dst = htonl(dst);
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	ip_src = (src ? htonl(src) : ifaddr);
	rtn = igmp_send(ifaddr,ip_src,ip,ip_dst,sizeof(struct ip)+sizeof(struct igmp)+8,IGMP_TTL,ROUTER_ALERT_OFF);
	if(trace_mode)
	    dvmrp_trace(NULL,ip_src,ip_dst,igmp,len+sizeof(struct igmp));
    }
    else {
        igmp_send2(igmp,ip_dst,sizeof(struct igmp)+8,IGMP_TTL,ROUTER_ALERT_OFF);
    }

}

/*
 *
 */
struct dvmrp_nbr *find_dvmrp_nbr(list,nbr)
struct dvmrp_nbr *list;
u_long nbr;
{
    struct dvmrp_nbr *l;

    for(l=list;l!=NULL;l=l->next) {
	if(l->addr == nbr) break;
    }

    return l;
}

/*
 *
 */
int vif_withaddr(addr)
u_long addr;
{
    int i;
    struct vif *vif;
    struct if_info *ifp;

    if(!(ifp = if_withaddr(addr)))
	return -1;

    for(i=0;i<vif_num;i++) {
	vif=vifs[i];
	if(vif->ifap == ifp) {
	    return i;
	}
    }

    return -1;
}

/*
 *
 */
void dvmrp_probe_trace(fp,igmp,igmp_len)
FILE *fp;
struct igmp *igmp;
int igmp_len;
{
    u_long *dvmrp,rtr;
    int len;
    char flag;

    flag = ntohl(igmp->igmp_group) >> 16;
    trace2(fp,"\n\t Flags: ");
    if(flag & DVMRP_CAP_LEAF) trace2(fp,"L");
    if(flag & DVMRP_CAP_PRUNE) trace2(fp,"P");
    if(flag & DVMRP_CAP_GENID) trace2(fp,"G");
    if(flag & DVMRP_CAP_MTRACE) trace2(fp,"M");
    if(flag & DVMRP_CAP_SNMP) trace2(fp,"S");

    dvmrp = (u_long *)(igmp+1);
    trace2(fp,"   Genid: %x ",ntohl(*dvmrp++));
    trace2(fp,"\n");

    len = igmp_len - sizeof(u_long) - sizeof(struct igmp);
    if(len) trace2(fp,"\t Routers:\n ");
    for(;len;len-=sizeof(u_long)) {
	rtr = ntohl(*dvmrp++);
	trace2(fp,"\t    %s\n", inet_atos(htonl(rtr)));
    }
    trace2(fp,"\n");
}

/*
 *
 */
void dvmrp_report_trace(fp,igmp,igmp_len)
FILE *fp;
struct igmp *igmp;
int igmp_len;
{
    char *dvmrp,rtr;
    int len;
    u_long net,mask;
    int octet,i,metric;

    dvmrp = (u_char *)(igmp+1);
    len = igmp_len - sizeof(struct igmp);
    if(len) trace2(fp,"\n\t Network: \n");
    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);
	trace2(fp,"\t %s :\n", inet_atos(htonl(mask)));   
	do {
	    net=0;
	    for(i=0;i<octet;i++)
		((char *)&net)[i] = *dvmrp++;
	    net = ntohl(net);
	    len -= octet;
	    metric = *dvmrp++;
	    metric &= 0x7f;
	    len--;
	    trace2(fp,"\t\t %s metric %d\n", inet_atos(htonl(net)),metric);
	 } while(!(*(dvmrp-1) & 0x80));
    }
    trace2(fp,"\n");
}

/*
 *
 */
void dvmrp_prune_trace(fp,igmp,igmp_len)
FILE *fp;
struct igmp *igmp;
int igmp_len;
{
    u_long *dvmrp,rtr;
    int len;

    dvmrp = (u_long *)(igmp+1);
    trace2(fp," src: %s ",inet_atos(*dvmrp++));
    trace2(fp," grp: %s ",inet_atos(*dvmrp++));
    trace2(fp," lifetime: %d",(int)ntohl(*dvmrp));
	
    trace(fp,"\n");
}

/*
 *
 */
void dvmrp_graft_trace(fp,igmp,igmp_len)
FILE *fp;
struct igmp *igmp;
int igmp_len;
{
    u_long *dvmrp,rtr;
    int len;

    dvmrp = (u_long *)(igmp+1);
    trace2(fp," src: %s ",inet_atos(*dvmrp++));
    trace2(fp," grp: %s ",inet_atos(*dvmrp));
	
    trace2(fp,"\n");
}

/*
 *
 */
void dvmrp_gack_trace(fp,igmp,igmp_len)
FILE *fp;
struct igmp *igmp;
int igmp_len;
{
    u_long *dvmrp,rtr;
    int len;

    dvmrp = (u_long *)(igmp+1);
    trace2(fp," src: %s ",inet_atos(*dvmrp++));
    trace2(fp," grp: %s ",inet_atos(*dvmrp));
	
    trace2(fp,"\n");
}

/*
 *
 */
void dvmrp_trace(fp,src,dst,igmp,igmp_len)
FILE *fp;
u_long src;
u_long dst;
struct igmp *igmp;
int igmp_len;
{
    struct in_addr addr;
    char *types[]={NULL,"Probe","Report",NULL,NULL,NULL,NULL,"Prune",
			      "Graft","Graft Ack",NULL};

    if(!trace_mode)
	return;

    ttrace(fp," %s > %s \t%s ",inet_ipstr(src,ipstr1),inet_ipstr(dst,ipstr2),types[igmp->igmp_code]);
    trace2(fp,"   Ver: %d", ntohl(igmp->igmp_group) & 0x000000ff);
    trace2(fp,".%d\n", ntohl(igmp->igmp_group)>>8 & 0x000000ff);

    if(!fp && trace_mode == TRACE_MODE_PKT) {
	return;
    }

    switch(igmp->igmp_code) {
    case DVMRP_PROBE: dvmrp_probe_trace(fp,igmp,igmp_len);
	break;
    case DVMRP_REPORT : dvmrp_report_trace(fp,igmp,igmp_len);
	break;
    case DVMRP_PRUNE : dvmrp_prune_trace(fp,igmp,igmp_len);
	break;
    case DVMRP_GRAFT:	dvmrp_graft_trace(fp,igmp,igmp_len);
	break;
    case DVMRP_GRAFT_ACK: dvmrp_gack_trace(fp,igmp,igmp_len);
	break;
    default : break;
    }

}

/*
 *
 */
void dvmrp_probe_recv(src,dst,igmp,igmp_len)
u_long src,dst;
struct igmp *igmp;
int igmp_len;
{
    u_long nbr;
    int len;
    u_long gen_id;
    int cap_flag,major,minor;
    u_long *dvmrp;
    struct if_info *ifp;
    struct vif *vif;
    int i,new_nbr;
    struct dvmrp_nbr *nbr_list,*nbrs;

    ifp = if_withaddr(src);
    if(!ifp) {
	printf(" No interface \n");
	return;
    }

    for(i=0;i<vif_num;i++) {
	vif = vifs[i];
	if(vif->ifap == ifp) break;
    }
    if(i == vif_num) return;

    new_nbr = 0;
    if(vif->nbrs) {
	nbr_list = vif->nbrs;
	nbrs = find_dvmrp_nbr(nbr_list,src);
	if(!nbrs) {
	  /*nbrs = (struct dvmrp_nbr *)malloc(sizeof(struct dvmrp_nbr));*/
	    MALLOC(nbrs,dvmrp_nbr);
	    add_vif_nbr_list(i,nbrs);
	    new_nbr = 1;
	}
    }
    else {
      /*nbrs = (struct dvmrp_nbr *)malloc(sizeof(struct dvmrp_nbr));*/
        MALLOC(nbrs,dvmrp_nbr);
	add_vif_nbr_list(i,nbrs);
	new_nbr = 1;
    }

    cap_flag = ntohl(igmp->igmp_group) >> 16;    
    dvmrp = (u_long *)(igmp+1);
    gen_id = ntohl(*dvmrp++);

    if(new_nbr) {
        nbrs->addr = src;
        nbrs->gen_id = gen_id;
        nbrs->cap = cap_flag;
        nbrs->vers = (short)(ntohl(igmp->igmp_group) & 0x0000ffff);
        nbrs->state = 0;
        nbrs->node_id = 0;
    }

    dvmrp_recv_func[DVMRP_PROBE].recv_routine(i,src,dst,nbrs,igmp,igmp_len);
}

/*
 *
 */
void dvmrp_report_recv(src,dst,igmp,igmp_len)
u_long src,dst;
struct igmp *igmp;
int igmp_len;
{
    u_long nbr;
    int len;
    u_long *dvmrp;
    struct if_info *ifp;
    struct vif *vif;
    int i,new_nbr;
    struct dvmrp_nbr *nbr_list,*nbrs;

    ifp = if_withaddr(src);
    if(!ifp) {
	printf(" No interface \n");
	return;
    }

    for(i=0;i<vif_num;i++) {
	vif = vifs[i];
	if(vif->ifap == ifp) break;
    }
    if(i == vif_num) return;

    new_nbr = 0;
    if(vif->nbrs) {
	nbr_list = vif->nbrs;
	nbrs = find_dvmrp_nbr(nbr_list,src);
	if(!nbrs) {
	    trace2(NULL," \n Report Recv Unknow NBR from vif %d -- ignore\n",i);
	}
    }
    else {
	trace2(NULL," \n Report Recv Unknow NBR -- ignore \n");
    }

    if(dvmrp_recv_func[DVMRP_REPORT].recv_routine)
	dvmrp_recv_func[DVMRP_REPORT].recv_routine(i,src,dst,nbrs,igmp,igmp_len);
    else
	printf(" Function not Register \n");
}

/*
 *
 */
void dvmrp_graft_recv(src,dst,igmp,igmp_len)
u_long src,dst;
struct igmp *igmp;
int igmp_len;
{
    u_long nbr;
    int len;
    u_long *dvmrp;
    struct if_info *ifp;
    struct vif *vif;
    int i,new_nbr;
    struct dvmrp_nbr *nbr_list,*nbrs;

    ifp = if_withaddr(src);
    if(!ifp) {
	printf(" No interface \n");
	return;
    }

    for(i=0;i<vif_num;i++) {
	vif = vifs[i];
	if(vif->ifap == ifp) break;
    }
    if(i == vif_num) return;

    new_nbr = 0;
    if(vif->nbrs) {
	nbr_list = vif->nbrs;
	nbrs = find_dvmrp_nbr(nbr_list,src);
	if(!nbrs) {
	    trace2(NULL,"\n GRAFT Recv Unknow NBR \n");
	}
    }
    else {
	trace2(NULL,"\n GRAFT Recv Unknow NBR \n");
    }

    if(dvmrp_recv_func[DVMRP_GRAFT].recv_routine)
	dvmrp_recv_func[DVMRP_GRAFT].recv_routine(i,src,dst,nbrs,igmp,igmp_len);
    else
	printf(" Function not Register \n");
}

/*
 *
 */
void dvmrp_graft_ack_recv(src,dst,igmp,igmp_len)
u_long src,dst;
struct igmp *igmp;
int igmp_len;
{
    u_long nbr;
    int len;
    u_long *dvmrp;
    struct if_info *ifp;
    struct vif *vif;
    int i,new_nbr;
    struct dvmrp_nbr *nbr_list,*nbrs;

    ifp = if_withaddr(src);
    if(!ifp) {
	printf(" No interface \n");
	return;
    }

    for(i=0;i<vif_num;i++) {
	vif = vifs[i];
	if(vif->ifap == ifp) break;
    }
    if(i == vif_num) return;

    new_nbr = 0;
    if(vif->nbrs) {
	nbr_list = vif->nbrs;
	nbrs = find_dvmrp_nbr(nbr_list,src);
	if(!nbrs) {
	    trace2(NULL,"\n GRAFT_ACK Recv Unknow NBR \n");
	}
    }
    else {
	trace2(NULL,"\n GRAFT_ACK Recv Unknow NBR \n");
    }

    if(dvmrp_recv_func[DVMRP_GRAFT_ACK].recv_routine)
	dvmrp_recv_func[DVMRP_GRAFT_ACK].recv_routine(i,src,dst,nbrs,igmp,igmp_len);
    else
	printf(" Function not Register \n");
}

/*
 *
 */
void dvmrp_recv(src,dst,pkt,pkt_len)
u_long src,dst;
char *pkt;
int pkt_len;
{
    struct igmp *igmp;
    int data_len, hdr_len;

    igmp = (struct igmp *)pkt;

    /* check if this packet from own */
    if(search_if_info_addr(src))
	return;

    if(!dvmrp_flag) 
        return;

    dvmrp_trace(NULL,htonl(src),htonl(dst),igmp,pkt_len);

    if(dvmrp_comp_func) {
	dvmrp_comp_func(src, dst, igmp->igmp_code, igmp, pkt_len);
    }

    switch(igmp->igmp_code) {
    case DVMRP_PROBE : dvmrp_probe_recv(src,dst,igmp,pkt_len);
	break;
    case DVMRP_REPORT: dvmrp_report_recv(src,dst,igmp,pkt_len);
	break;
    case DVMRP_GRAFT: dvmrp_graft_recv(src,dst,igmp,pkt_len);
	break;
    case DVMRP_GRAFT_ACK: dvmrp_graft_ack_recv(src,dst,igmp,pkt_len);
	break;
    default : break;
    }

}

/*
 *
 */
void vif_dump()
{
    struct vif *vif;
    int i;
    struct dvmrp_nbr *nbr_list;

    printf("\n Virtual Interface \n");
    for(i=0;i<vif_num;i++) {
	vif = vifs[i];
	printf(" \t vif %d : ",i);
	printf(" %s ",inet_atos(htonl(vif->lcl_addr)));
	printf("(%s) \n",IF_NAME(vif->ifap->ipaddr));
	for(nbr_list=vif->nbrs;nbr_list!=NULL;nbr_list=nbr_list->next) {
	    printf(" \t   Neighbors %s \n",inet_atos(htonl(nbr_list->addr)));
	}
    }

}
