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

#include "defs.h"
#include <net/route.h>
#include <netinet/ip_mroute.h>
#include "pim_def.h"
#include "interface.h"

#define MAXVIFS   32
#define BUF_SIZE  1024

#if (defined(_BSDI_VERSION) || defined(SOLARIS))
/*
 * Struct used to communicate from kernel to multicast router
 * note the convenient similarity to an IP packet
 */
struct igmpmsg {
    u_long          unused1;
    u_long          unused2;
    u_char          im_msgtype;                 /* what type of message     */
#define IGMPMSG_NOCACHE         1
#define IGMPMSG_WRONGVIF        2
    u_char          im_mbz;                     /* must be zero             */
    u_char          im_vif;                     /* vif rec'd on             */
    u_char          unused3;
    struct in_addr  im_src, im_dst;
};
#endif

struct pim_header {
    u_char  type;
    u_char  reserved;
    u_short cksum;
};

struct pim_hello {
    u_short opt_type;
    u_short opt_len;
};

struct pim_eaddr {
    u_char ea_family;
    u_char ea_type;
    u_char ea_flags;
    u_char ea_masklen;
    u_long ea_addr;
};

struct pim_euaddr {
    u_char eu_family;
    u_char eu_type;
    u_long eu_addr;
};

struct pim_bsr {
    u_short frag_tag;
    u_char  hashmask;
    u_char  priority;
};

struct pim_crp {
    u_char prefix_cnt;
    u_char priority;
    u_short holdtime;
};

struct rp_list {
    struct rp_list *next;
    u_long addr;
    u_short holdtime;
    u_short priority;
};

struct grp_list {
    struct grp_list *next;
    struct grp_list *grp;
    struct rp_list *rp;
    u_long addr;
    u_long mask;
    int rp_cnt;
};

struct rpset_list {
    struct rpset_list *next;
    int id;
    struct grp_list *grps;
};

struct bsr_list {
    struct bsr_list *next;
    int id;
    u_long addr;
    int priority;
    struct grp_list *rpset;
    struct rpset_list *rps;
};

struct src_list {
    struct src_list *next;
    u_char family;
    u_char type;
    u_char flags;
    u_long mask;
    u_long addr;
};
    
struct jp_entry {
    struct jp_entry *next;
    u_long grp_addr;
    u_long grp_mask;
    u_short j_num;
    u_short p_num;
    struct src_list *join;
    struct src_list *prune;
};

struct jp_list {
    struct jp_list *next;
    int id;
    u_long nbr_addr;  /* upstream neighbor */
    int holdtime;
    struct jp_entry *jpe;
};

struct crp_list {
    struct crp_list *next;
    int id;
    u_long rpaddr;
    int priority;
    int holdtime;
    struct grp_list *grps;
};

struct pim_nbr {
    struct pim_nbr *next;
    u_long addr;
    short opt_type;   /* Option type : holdtime */
    short opt_len;
    u_long opt_val;
    int state;
};

struct vif_info {
    u_long lcl_addr;
    u_long rmt_addr;
    u_long netmask;
    struct if_info *ifap;
    struct pim_nbr *nbrs;
};

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

struct _igmp_recv_func {
    void (*recv_routine)();
    int dummry;
};

static struct _pim_recv_func pim_recv_func[PIM_MAX_TYPE];
extern struct _igmp_recv_func igmp_sg_func;
void (*pim_comp_func)();

int pim_sock;
int vif_num=0;

char *send_buf;
char *recv_buf;
char *inet_atos();
char *inet_ipstr();
struct if_info *if_withaddr();
void pim_trace();

extern int trace_mode;
extern struct if_info *if_list;
struct grp_list *grplist=NULL;
struct jp_entry *jp_entry_list=NULL;
struct jp_list *jplist=NULL;
struct bsr_list *bsrlist=NULL;
struct vif_info *vifs[MAXVIFS];
struct rpset_list *rpsetlist=NULL;
struct crp_list *crplist=NULL;

/*
 *
 */
void pim_init()
{
    struct vif_info *vif;
    struct if_info *ifp;
    int i;

    if((pim_sock = socket(AF_INET, SOCK_RAW, IPPROTO_PIM)) < 0) {
        perror("PIM socket");
	exit(1);
    }
    send_buf = malloc(BUF_SIZE);
    bzero(send_buf,BUF_SIZE);

    for(ifp=if_list;ifp;ifp=ifp->next) {
	MALLOC(vif, vif_info);
	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<PIM_MAX_TYPE;i++)
	pim_recv_func[i].recv_routine = NULL;
    pim_comp_func = NULL;

}

#if 1
void pim_if_init(addr)
u_long addr;
{
    struct ip_mreq mreq;
    int i;
    struct vif_info *vifp;
    struct if_info *ifp;

    i = vif_withaddr(addr);
    if(i>=0) {
	vifp = vifs[i];
	ifp = vifp->ifap;
    }
    else
	return;

    if(!ifp->add_flag) {
	mreq.imr_multiaddr.s_addr = htonl(ALL_PIM_ROUTERS);
	mreq.imr_interface.s_addr = htonl(addr);

	if(setsockopt(pim_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,
		      (char *)&mreq,sizeof(mreq))<0)
	    perror("can't join group");
	ifp->add_flag = 1;
    }
}
#endif

void pim_recv_register(type, callback)
int type;
void callback();
{
    pim_recv_func[type].recv_routine = callback;
}

void pim_comp_register(callback)
void callback();
{
    pim_comp_func = callback;
}

#define PIM_SEND(if_name,src,dst) { \
    if(if_name) { \
        ifaddr = htonl(get_ifaddr_byname(if_name)); \
	src = (src ? htonl(src) : ifaddr); \
	rtn = pim_send(ifaddr,src,ip,dst,sizeof(struct ip)+pim_len,PIM_TTL); \
    } \
    else { \
        pim_send2(pim,dst,pim_len,PIM_TTL); \
    } \
}

/*
 *
 */
int pim_hello_send(if_name,src,dst,type,len,value)
char *if_name;
u_long src,dst;
u_int type,len;
u_long value;
{
    struct ip *ip;
    struct pim_header *pim;
    struct pim_hello *hello;
    char *opt;
    int rtn;
    u_long ifaddr;
    int pim_len;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }

    hello = (struct pim_hello *)(pim+1);
    hello->opt_type = htons(type);
    hello->opt_len = htons(len);
    opt = (char *)(hello+1);
    while(len--) 
	*opt++ = (u_char)(value>>(8*len));

    pim_len = opt - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_HELLO);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
    PIM_SEND(if_name,src,dst);
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
#else
    pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int add_rp_list(grp,addr,holdtime,priority)
struct grp_list *grp;
u_long addr;
u_short holdtime;
u_short priority;
{
    struct rp_list *new_rp,*listp;

    if(grp && grp->rp) {
        for(listp=grp->rp;listp;listp=listp->next) {
	    if(listp->addr == addr)
	        return 0;
	}
    }

    MALLOC(new_rp,rp_list);
    new_rp->addr = addr;
    new_rp->holdtime = holdtime;
    new_rp->priority = priority;

    if(grp->rp) {
        for(listp=grp->rp;listp->next;listp=listp->next);
	listp->next = new_rp;
    }
    else {
        grp->rp = new_rp;
    }

    return 1;
}

/*
 *
 */
void add_rpset_list2(id,grp,mask,rp_addr,holdtime,priority)
int id;
u_long grp;
u_long mask;
u_long rp_addr;
u_short holdtime;
u_short priority;
{
    struct grp_list *new_grp,*gp;
    struct rpset_list *new_rpset,*rpset;
    struct rp_list *rp;

    for(new_rpset=rpsetlist;new_rpset;new_rpset=new_rpset->next) {
        if(new_rpset->id == id) break;
    };

    if(!new_rpset) {
	MALLOC(new_rpset, rpset_list);
	MALLOC(new_grp, grp_list);
	new_rpset->id = id;
	new_rpset->grps = new_grp;
	new_grp->addr = grp;
	new_grp->mask = mask;
	new_grp->rp = NULL;
	new_grp->rp_cnt = 0;
	if(rpsetlist) {
	    for(rpset=rpsetlist;rpset->next;rpset=rpset->next) ;
	    rpset->next = new_rpset;
	}
	else {
	    rpsetlist = new_rpset;
	}
	if(!id)
	    grplist = new_grp;
    }
    else {
	for(new_grp = new_rpset->grps;new_grp!=NULL;new_grp=new_grp->next) {
	    if(new_grp->addr == grp) break;
	}
	if(!new_grp) {
	    MALLOC(new_grp, grp_list);
	    new_grp->addr = grp;
	    new_grp->mask = mask;
	    new_grp->rp = NULL;
	    new_grp->rp_cnt = 0;
	    for(gp=new_rpset->grps;gp->next!=NULL;gp=gp->next);
	    gp->next = new_grp;
	}
    }

    if(add_rp_list(new_grp,rp_addr,holdtime,priority))
        new_grp->rp_cnt++;

}

/*
 * 
 */
void add_rpset_group(id,addr,mask)
int id;
u_long addr;
u_long mask;
{
    struct rpset_list *rpset;
    struct grp_list *grp,*new_grp;

    for(rpset=rpsetlist;rpset!=NULL;rpset=rpset->next) {
	if(rpset->id == id) break;
    }
    if(!rpset) return;

    MALLOC(new_grp, grp_list);
    new_grp->addr = addr;
    new_grp->mask = mask;
    new_grp->rp = NULL;
    new_grp->rp_cnt = 0;
    if(rpset->grps) {
	for(grp=rpset->grps;grp->next!=NULL;grp=grp->next);
	grp->next = new_grp;
    }
    else {
	rpset->grps = new_grp;
    }
}

/*
 *
 */
u_long search_crp_withgrp(grp,mask)
u_long grp;
u_long mask;
{
    struct grp_list *new_grp,*gp;
    struct rp_list *rp;
    struct rp_list *new_rp,*listp;

    if(!grplist) 
	return 0;

    for(gp=grplist;gp->next;gp=gp->next) {
	if(gp->addr == grp & gp->mask) break;
    }
    if(!gp)
	return 0;

    rp = gp->rp;

    return rp->addr;
}

/*
 *
 */
void print_rpset_list()
{
    struct grp_list *grp;
    struct rp_list *rp;

    for(grp=grplist;grp;grp=grp->next) {
        printf(" Group %s ",inetaddr_to_char(htonl(grp->addr)));
	printf(" Mask %s ",inetaddr_to_char(htonl(grp->mask)));
	printf(" RPs %d \n",grp->rp_cnt);
	for(rp=grp->rp;rp;rp=rp->next) {
 	    printf("     %s %d\n",inetaddr_to_char(htonl(rp->addr)),rp->holdtime);
	}
    }
}

/*
 *
 */
void add_bsr_list(id,addr,priority)
int id;
u_long addr;
int priority;
{
    struct bsr_list *bsrp,*new;

/*    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_INFO,0," add_bsr_list %d <%x %d>\n",id,addr,priority);
*/
    for(bsrp=bsrlist;bsrp!=NULL;bsrp=bsrp->next) {
	if(bsrp && bsrp->id == id)
	    return ;
    }

    MALLOC(new, bsr_list);
    new->id = id;
    new->addr = addr;
    new->priority = priority;
    new->rpset = NULL;

    if(!bsrlist) {
	bsrlist = new;
    }
    else {
	for(bsrp=bsrlist;bsrp->next!=NULL;bsrp=bsrp->next);
	bsrp->next = new;
    }
}

/*
 *
 */
void add_bsr_rpset(id,list)
int id;
int list;
{
    struct bsr_list *bsrp;
    struct rp_list *rp;
    struct rpset_list *rpset;

    for(bsrp=bsrlist;bsrp!=NULL;bsrp=bsrp->next) {
	if(bsrp->id == id) 
	    break;
    }
    if(!bsrp)
	return;

    for(rpset=rpsetlist;rpset!=NULL;rpset=rpset->next) {
	if(rpset->id == list)
	    break;
    }
    if(!rpset)
	return;

    bsrp->rps = rpset;

}

/*
 *
 */
struct grp_list *search_grp_list(addr,mask)
u_long addr;
u_long mask;
{
    struct grp_list *grp;

    for(grp=grplist;grp;grp=grp->next) {
	if(grp->addr == addr && grp->mask == mask)
	    return grp;
    }

    return NULL;
}

/*
 *
 */
void add_grp_list(grp,mask)
u_long grp;
u_long mask;
{
    struct grp_list *new_grp,*gp;
    struct rp_list *rp;

    for(new_grp=grplist;new_grp;new_grp=new_grp->next) {
        if(new_grp->addr == grp) break;
    };

    if(!new_grp) {
	MALLOC(new_grp, grp_list);
	new_grp->addr = grp;
	new_grp->mask = mask;
	new_grp->rp = NULL;
	new_grp->rp_cnt = 0;
	if(grplist) {
	    for(gp=grplist;gp->next;gp=gp->next) ;
	    gp->next = new_grp;
	}
	else {
	    grplist = new_grp;
	}
    }

}

/*
 *
 */
void add_jp_list_nbr(id,nbr,holdtime)
int id;
u_long nbr;
int holdtime;
{
    struct jp_entry *new_entry,*jpe,*pre;
    struct jp_list *jplp,*new_jplp;

    for(jplp=jplist;jplp;jplp=jplp->next) {
        if(jplp->id == id) break;
    };

    if(!jplp) {
	MALLOC(new_jplp, jp_list);
	new_jplp->id = id;
	new_jplp->nbr_addr = nbr;
	new_jplp->holdtime = holdtime;
	new_jplp->jpe = NULL;
	if(jplist) {
	    log(LOG_INFO,0," Add New jp-list entry %d (nbr)\n",new_jplp->id);
	    for(jplp=jplist;jplp->next;jplp=jplp->next) ;
	    jplp->next = new_jplp;
	}
	else {
	/*    if(IS_DEBUG(DEBUG_TRACE))
		log(LOG_INFO,0," First jp-list entry %d (nbr) \n",new_jplp->id);*/
	    jplist = new_jplp;
	}
    }
    else {
	jplp->nbr_addr = nbr;
	jplp->holdtime = holdtime;
    }

}

/*
 *
 */
void add_jp_list(id,grp,mask)
int id;
u_long grp;
u_long mask;
{
    struct jp_entry *new_entry,*jpe,*pre;
    struct jp_list *jplp,*new_jplp;

    for(jplp=jplist;jplp;jplp=jplp->next) {
        if(jplp->id == id) break;
    };

    if(!jplp) {
	MALLOC(new_jplp,jp_list);
	new_jplp->id = id;
	new_jplp->nbr_addr = 0;
	new_jplp->holdtime = 0;
	MALLOC(jpe, jp_entry);
	jpe->grp_addr = grp;
	jpe->grp_mask = mask;
	jpe->join = jpe->prune = NULL;
	jpe->j_num = jpe->p_num = 0;
	new_jplp->jpe = jpe;
	if(jplist) {
	    log(LOG_INFO,0," Add New jp-list entry %d\n",new_jplp->id);
	    for(jplp=jplist;jplp->next;jplp=jplp->next) ;
	    jplp->next = new_jplp;
	}
	else {
	    log(LOG_INFO,0," First jp-list entry %d\n",new_jplp->id);
	    jplist = new_jplp;
	}
    }
    else {
	if(jplp->jpe) {
	    for(jpe=jplp->jpe;jpe;jpe=jpe->next) {
		if(jpe->grp_addr == grp && jpe->grp_mask == mask) {
		    /*if(IS_DEBUG(DEBUG_TRACE))
			log(LOG_INFO,0," Same instanse (%s) exist on %d\n",inet_atos(htonl(jpe->grp_addr)),jplp->id);*/
		    return ;
		}
		pre = jpe;
	    }
	    MALLOC(new_entry, jp_entry);
	    new_entry->grp_addr = grp;
	    new_entry->grp_mask = mask;
	    new_entry->join = new_entry->prune = NULL;
	    new_entry->j_num = new_entry->p_num = 0;
	    pre->next = new_entry;
	}
	else {
	    MALLOC(new_entry, jp_entry);
	    new_entry->grp_addr = grp;
	    new_entry->grp_mask = mask;
	    new_entry->join = new_entry->prune = NULL;
	    new_entry->j_num = new_entry->p_num = 0;
	    jplp->jpe = new_entry;
	}
    }

}

/*
 *
 */
void add_jp_join_list2(id,grp,src,mask,flags)
int id;
u_long grp;
u_long src;
u_long mask;
u_char flags;
{
    struct jp_entry *new_grp,*jp;
    struct jp_list *jplp;
    struct src_list *sp,*sp1;

    for(jplp=jplist;jplp;jplp=jplp->next)
        if(jplp->id == id) break;
    if(!jplp) {
	printf(" No define jp-list %d\n",id);
	return;
    }

    for(jp=jplp->jpe;jp;jp=jp->next)
	if(jp->grp_addr == grp) break;
    if(!jp) {
	printf(" Not found group %s jp-list %d \n",inet_atos(htonl(grp)),id);
	return;
    }

    MALLOC(sp1, src_list);
    sp1->addr = src;
    sp1->mask = mask;
    sp1->flags = flags;

    if(jp->join) {
        for(sp=jp->join;sp->next;sp=sp->next) ;
	sp->next = sp1;
    }
    else {
        jp->join = sp1;
    }
    jp->j_num++;
}

/*
 *
 */
void add_jp_prune_list2(id,grp,src,mask,flags)
int id;
u_long grp;
u_long src;
u_long mask;
u_char flags;
{
    struct jp_entry *new_grp,*jp;
    struct jp_list *jp_listp;
    struct src_list *sp,*sp1;

    for(jp_listp=jplist;jp_listp;jp_listp=jp_listp->next)
        if(jp_listp->id == id)
	    break;
    if(!jp_listp) {
	printf(" No define jp-list %d\n",id);
	return;
    }

    for(jp=jp_listp->jpe;jp;jp=jp->next)
	if(jp->grp_addr == grp)
	    break;
    if(!jp) {
	printf(" Not found group %s jp-list %d\n",inet_atos(htonl(grp)),id);
	return;
    }

    MALLOC(sp1,src_list);
    sp1->addr = src;
    sp1->mask = mask;
    sp1->flags = flags;

    if(jp->prune) {
        for(sp=jp->prune;sp->next;sp=sp->next) ;
	sp->next = sp1;
    }
    else {
        jp->prune = sp1;
    }
    jp->p_num++;
}

#define PRINT_US_FLAGS(flags)                         \
        do {                                         \
             printf("[");                            \
	     if(flags & USADDR_S_BIT) printf("S");   \
	     if(flags & USADDR_WC_BIT) printf("W");  \
	     if(flags & USADDR_RP_BIT) printf("R");  \
             printf("]");                            \
	} while(0) 
                 
/*
 *
 */
void print_jp_list()
{
    struct jp_entry *jpe;
    struct jp_list *jpl;
    struct src_list *sp;

    printf(" JP List:\n");
    for(jpl=jplist;jpl;jpl=jpl->next) {
	printf("\t Id: %d  upstream: %s  holdtime: %d \n",jpl->id, inet_atos(htonl(jpl->nbr_addr)),jpl->holdtime);
	for(jpe=jpl->jpe;jpe;jpe=jpe->next) {
	    printf("\t      %s\n ",inet_atos(htonl(jpe->grp_addr)));
	    printf("\t\t joins:%d   ",jpe->j_num);
	    if(sp=jpe->join) { 
		printf("%s/%d ",inet_atos(htonl(sp->addr)),mask_bits(sp->addr,sp->mask));
		PRINT_US_FLAGS(sp->flags);
		printf("\n");
		for(sp=sp->next;sp;sp=sp->next) {
		    printf("\t\t\t   %s/%d ",inet_atos(htonl(sp->addr)),mask_bits(sp->addr,sp->mask));
		    PRINT_US_FLAGS(sp->flags);
		    printf("\n");
		}
	    }
	    else printf("\n");
	    printf("\t\t prunes:%d  ",jpe->p_num);
	    if(sp=jpe->prune) {
		printf("%s/%d ",inet_atos(htonl(sp->addr)),mask_bits(sp->addr,sp->mask));
		PRINT_US_FLAGS(sp->flags);
		printf("\n");
		for(sp=sp->next;sp;sp=sp->next) {
		    printf("\t\t\t   %s/%d ",inet_atos(htonl(sp->addr)),mask_bits(sp->addr,sp->mask));
		    PRINT_US_FLAGS(sp->flags);
		    printf("\n");
		}
	    }
	    else printf("\n");
	}
	printf("\n");
    }
}

/*
 *
 */
void add_crp_list(id, addr, holdtime, pri)
int id;
u_long addr;
int holdtime;
int pri;
{
    struct crp_list *crp,*new_crp;

    for(new_crp=crplist;new_crp!=NULL;new_crp!=new_crp->next) {
	if(new_crp->id == id) break;
    }

    if(!new_crp) {
	MALLOC(new_crp, crp_list);
	new_crp->id = id;
	new_crp->rpaddr = addr;
	new_crp->holdtime = holdtime;
	new_crp->priority = pri;
	new_crp->grps = NULL;
	if(crplist) {
	    for(crp=crplist;crp->next!=NULL;crp=crp->next);
	    crp->next = new_crp;
	}
	else
	    crplist = new_crp;
    }
}

/*
 *
 */
void add_crp_group(id,addr,mask)
int id;
u_long addr;
u_long mask;
{
    struct crp_list *crp;
    struct grp_list *grp,*new_grp;

    for(crp=crplist;crp!=NULL;crp=crp->next) {
	if(crp->id == id) break;
    }
    if(!crp) return;

    MALLOC(new_grp, grp_list);
    new_grp->addr = addr;
    new_grp->mask = mask;
    new_grp->rp = NULL;
    new_grp->rp_cnt = 0;
    if(crp->grps) {
	for(grp=crp->grps;grp->next!=NULL;grp=grp->next);
	grp->next = new_grp;
    }
    else {
	crp->grps = new_grp;
    }
}

/*
 *
 */ 
int pimsm_bsr_send(if_name,src,dst,bsr_addr,priority)
char *if_name;
u_long src,dst;
u_long bsr_addr;
u_short priority;
{
    char *cp;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_bsr *bsr;
    u_long ifaddr;
    struct grp_list *grp;
    struct rp_list *rp;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }

    cp = (char *)(pim+1);

    PUT_HOSTSHORT(0,cp);
    *cp++ = 30;                 /* Hash mask length */
    *cp++ = (u_char)priority;   /* BSR's priority */

    PUT_EUADDR(htonl(bsr_addr),cp);
    for(grp=grplist;grp;grp=grp->next) {
        PUT_EADDR(htonl(grp->addr),grp->mask,cp);
	*cp++ = grp->rp_cnt;
	*cp++ = 0;            /* Frag RP cnt */
	PUT_HOSTSHORT(0,cp);  /* Reserved */

	for(rp=grp->rp;rp;rp=rp->next) {
	    PUT_EUADDR(htonl(rp->addr),cp);  /* Encode-Unicast RP Addr */
	    PUT_HOSTSHORT(rp->holdtime,cp);
#if 1
	    *cp++ = (u_char)rp->priority;    /* Priority */  
	    *cp++ = 0;                       /* Reserved */
#endif
	}
    }

    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_BOOTSTRAP);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */ 
int pimsm_bsr_send2(if_name,src,dst,bsr_addr,priority,list)
char *if_name;
u_long src,dst;
u_long bsr_addr;
u_short priority;
int list;
{
    char *cp;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_bsr *bsr;
    u_long ifaddr;
    struct grp_list *grp;
    struct rp_list *rp;
    int pim_len,rtn;
    struct bsr_list *bsrp;
    struct rpset_list *rpset;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);
    
    PUT_HOSTSHORT(0,cp);
    *cp++ = 30;                 /* Hash mask length */
    if(priority)
	*cp++ = (u_char)priority;   /* BSR's priority */
    else {
	if(bsrlist) {
	    bsrp=bsrlist;
	    *cp++ = (u_char)bsrp->priority;
	}
	else {
	    printf(" No BSR Address \n");
	    return;
	}
    }

    if(bsr_addr)
	PUT_EUADDR(htonl(bsr_addr),cp);
    else
	PUT_EUADDR(htonl(bsrp->addr),cp);

    rpset = bsrp->rps;
    for(grp=rpset->grps;grp;grp=grp->next) {
        PUT_EADDR(htonl(grp->addr),grp->mask,cp);
	*cp++ = grp->rp_cnt;
	*cp++ = 0;            /* Frag RP cnt */
	PUT_HOSTSHORT(0,cp);  /* Reserved */

	for(rp=grp->rp;rp;rp=rp->next) {
	    PUT_EUADDR(htonl(rp->addr),cp);  /* Encode-Unicast RP Addr */
	    PUT_HOSTSHORT(rp->holdtime,cp);
#if 1
	    *cp++ = (u_char)rp->priority;    /* Priority */  
	    *cp++ = 0;                       /* Reserved */
#endif
	}
    }

    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_BOOTSTRAP);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));
    
#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int pim_adv_bsr(if_name,src,dst,list)
char *if_name;
u_long src,dst;
int list;
{
    struct bsr_list *bsrp;
    int len;

    for(bsrp=bsrlist;bsrp!=NULL;bsrp=bsrp->next) {
	if(bsrp->id == list)
	    break;
    }
    if(!bsrp) {
	printf(" Warning !! bsrp is NULL \n");
	return;
    }
    len = pimsm_bsr_send(if_name,src,dst,bsrp->addr,bsrp->priority);
    
    return len;
}

/*
 *
 */
void pimsm_crp_send(if_name,src,dst,rp_addr,holdtime,priority)
char *if_name;
u_long src,dst,rp_addr;
u_short holdtime;
u_char priority;
{
    char *cp,*cnt,prefix_cnt;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    struct grp_list *grp;
    struct rp_list *rp;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cnt = cp = (char *)(pim+1);

    *cp++ = 0;
    *cp++ = (u_char)priority;
    PUT_HOSTSHORT(holdtime,cp);
    PUT_EUADDR(htonl(rp_addr),cp);

    prefix_cnt=0;
    for(grp=grplist;grp;grp=grp->next) {
        PUT_EADDR(htonl(grp->addr),grp->mask,cp);
	prefix_cnt++;
    }
    /* defaut 224.0.0.0/4 */
    if(!grplist) {
        PUT_EADDR(htonl(0xe0000000),0xff000000,cp);
    }

    *cnt = prefix_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_CRP_ADV);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	src = (src ? htonl(src) : ifaddr);
	rtn = pim_send(ifaddr,src,ip,dst,sizeof(struct ip)+pim_len,255); /* crp_adv unicast to BSR, so TTL > 1 */
    }
    else {
        pim_send2(pim,dst,pim_len,255);
    }
#else
    pim_send2(pim,dst,pim_len,255);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);
}

/*
 *
 */
void pimsm_crp_send2(if_name,src,dst,rp_addr,holdtime,priority,list)
char *if_name;
u_long src,dst,rp_addr;
u_short holdtime;
u_char priority;
int list;
{
    char *cp,*cnt,prefix_cnt;
    struct ip *ip;
    struct pim_header *pim;
    u_long ifaddr;
    struct grp_list *grp;
    struct rp_list *rp;
    int pim_len,rtn;
    struct crp_list *crp;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }

    for(crp=crplist;crp!=NULL;crp=crp->next) {
	if(crp->id == list) break;
    }
    if(!crp) {
	printf(" no crp information \n");
	return;
    }
    if(!priority) priority = crp->priority;
    if(!holdtime) holdtime = crp->holdtime;
    if(!rp_addr) rp_addr = crp->rpaddr;

    cnt = cp = (char *)(pim+1);

    *cp++ = 0;
    *cp++ = (u_char)priority;
    PUT_HOSTSHORT(holdtime,cp);
    PUT_EUADDR(htonl(rp_addr),cp);

    prefix_cnt=0; 
    for(grp=crp->grps;grp;grp=grp->next) {
        PUT_EADDR(htonl(grp->addr),grp->mask,cp);
	prefix_cnt++;
    }
#if 0 
    /* defaut 224.0.0.0/4 --- Alwayes need ? */
    if(!grplist) {
        PUT_EADDR(htonl(0xe0000000),0xff000000,cp);
    }
#endif
    *cnt = prefix_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_CRP_ADV);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	src = (src ? htonl(src) : ifaddr);
	rtn = pim_send(ifaddr,src,ip,dst,sizeof(struct ip)+pim_len,255); /* crp_adv unicast to BSR, so TTL > 1 */
    }
    else {
        pim_send2(pim,dst,pim_len,255);
    }
#else
    pim_send2(pim,dst,pim_len,255);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

}

/*
 *
 */
int pimsm_jp_send(if_name,src,dst,up_addr,holdtime)
char *if_name;
u_long src,dst,up_addr;
u_short holdtime;
{
    char *cp,*cnt,grp_cnt;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    struct src_list *sp;
    struct jp_entry *jp;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);

    PUT_EUADDR(htonl(up_addr),cp);    
    *cp++ = 0;
    cnt = cp;
    *cp++;

    PUT_HOSTSHORT(holdtime,cp);

    grp_cnt=0;
    for(jp=jp_entry_list;jp;jp=jp->next) {
        PUT_EADDR(htonl(jp->grp_addr),jp->grp_mask,cp);
	grp_cnt++;
	PUT_HOSTSHORT(jp->j_num,cp);
	PUT_HOSTSHORT(jp->p_num,cp);
	for(sp=jp->join;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
	for(sp=jp->prune;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
    }

    *cnt = grp_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_JOIN_PRUNE);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int pimsm_jp_send2(if_name,src,dst,up_addr,holdtime,list)
char *if_name;
u_long src,dst,up_addr;
u_short holdtime;
int list;
{
    char *cp,*cnt,grp_cnt;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    struct src_list *sp;
    struct jp_list *jpl;
    struct jp_entry *jp;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);

    for(jpl=jplist;jpl;jpl=jpl->next) {
	if(jpl->id == list) break;
    }    
    if(!jpl)  return 0;

    if(up_addr)
	PUT_EUADDR(htonl(up_addr),cp);    
    else {
	if(jpl->nbr_addr)
	    PUT_EUADDR(htonl(jpl->nbr_addr),cp);
	else
	    printf(" No Upstream Neighbor \n");
    }
    *cp++ = 0;
    cnt = cp;
    *cp++;

    if(holdtime)
	PUT_HOSTSHORT(holdtime,cp);
    else {
	if(jpl->holdtime)
	    PUT_HOSTSHORT(jpl->holdtime,cp);
	else
	    PUT_HOSTSHORT(0,cp);
    }

    grp_cnt=0;
    for(jp=jpl->jpe;jp;jp=jp->next) {
	PUT_EADDR(htonl(jp->grp_addr),jp->grp_mask,cp);
	grp_cnt++;
	PUT_HOSTSHORT(jp->j_num,cp);
	PUT_HOSTSHORT(jp->p_num,cp);
	for(sp=jp->join;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
	for(sp=jp->prune;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
    }

    *cnt = grp_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_JOIN_PRUNE);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int pim_adv_jp(if_name,src,dst,list)
char *if_name;
u_long src,dst;
int list;
{
    struct jp_list *jpl;
    int len;

    for(jpl=jplist;jpl!=NULL;jpl=jpl->next) {
	if(jpl->id == list)
	    break;
    }
    if(!jpl) {
	printf(" Warning !! jpl is NULL \n");
	return;
    }
    len = pimsm_jp_send2(if_name,src,dst,jpl->nbr_addr,jpl->holdtime,list);
    
    return len;
}

/*
 *
 */
void pimsm_register_send(if_name,src,dst,pkt,n_bit,b_bit)
char *if_name;
u_long src,dst;
char *pkt;
u_long n_bit,b_bit;
{
    char *cp;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    int pim_len,rtn;
    int pkt_len;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);
    PUT_HOSTLONG(b_bit | n_bit, cp);
#if 0
    *cp = 0;
    *cp++ = b_bit | n_bit ;
    *cp++ = 0;           /* reserved */
    PUT_HOSTSHORT(0,cp);  /* reserved */
#endif
    pim_len = cp - (char *)pim;
    
    if(pkt) {
	pkt_len = ntohs(((struct ip *)pkt)->ip_len);
	bcopy(pkt,cp,pkt_len);
	pim_len += pkt_len;
    }
    pim->type = (PIM_VERSION << 4 | PIM_REGISTER);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,sizeof(struct pim_header)+4); 
    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	src = (src ? htonl(src) : ifaddr);
	rtn = pim_send(ifaddr,src,ip,dst,sizeof(struct ip)+pim_len,32); /* Register unicast to RP, TTL > 1 */
    }
    else {
        pim_send2(pim,dst,pim_len,32);
    }
#else
    pim_send2(pim,dst,pim_len,32);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);
}

/*
 *
 */
void pimsm_register_stop_send(if_name,src,dst,en_src,grp,mask)
char *if_name;
u_long src,dst;
u_long en_src;
u_long grp,mask;
{
    char *cp;
    struct ip *ip;
    struct pim_header *pim;
    u_long ifaddr;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);
    PUT_EADDR(htonl(grp),mask, cp);
    PUT_EUADDR(htonl(en_src),cp); 

    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_REGISTER_STOP);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
    if(if_name) {
        ifaddr = htonl(get_ifaddr_byname(if_name));
	src = (src ? htonl(src) : ifaddr);
	rtn = pim_send(ifaddr,src,ip,dst,sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,dst,pim_len,PIM_TTL);
    }
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

}

/*
 *
 */
void pimsm_assert_send(if_name,src,dst,en_src,grp,mask,pref,metric,r_bit)
char *if_name;
u_long src,dst;
u_long en_src;
u_long grp,mask;
u_long pref,metric;
int r_bit;
{
    char *cp;
    struct ip *ip;
    struct pim_header *pim;
    u_long ifaddr;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);
    PUT_EADDR(htonl(grp),mask, cp);
    PUT_EUADDR(htonl(en_src),cp); 

    if(r_bit) pref |= ASSERT_RPT_BIT;
    PUT_HOSTLONG(pref,cp);
    PUT_HOSTLONG(metric,cp);

    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_ASSERT);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);
}

/*
 *
 */
int pim_graft_send(if_name,src,dst,up_addr,holdtime)
char *if_name;
u_long src,dst,up_addr;
u_short holdtime;
{
    char *cp,*cnt,grp_cnt;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    struct src_list *sp;
    struct jp_entry *jp;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);

    PUT_EUADDR(htonl(up_addr),cp);    
    *cp++ = 0;
    cnt = cp;
    *cp++;

    PUT_HOSTSHORT(holdtime,cp);

    grp_cnt=0;
    for(jp=jp_entry_list;jp;jp=jp->next) {
        PUT_EADDR(htonl(jp->grp_addr),jp->grp_mask,cp);
	grp_cnt++;
	PUT_HOSTSHORT(jp->j_num,cp);
	PUT_HOSTSHORT(jp->p_num,cp);
	for(sp=jp->join;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
	for(sp=jp->prune;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
    }

    *cnt = grp_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_GRAFT);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int pim_graft_send2(if_name,src,dst,up_addr,holdtime,list)
char *if_name;
u_long src,dst,up_addr;
u_short holdtime;
int list;
{
    char *cp,*cnt,grp_cnt;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    struct src_list *sp;
    struct jp_entry *jp;
    struct jp_list *jpl;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    for(jpl=jplist;jpl;jpl=jpl->next) {
	if(jpl->id == list) break;
    }
    if(!jpl) return 0;

    cp = (char *)(pim+1);

    if(up_addr)
	PUT_EUADDR(htonl(up_addr),cp);    
    else {
	if(jpl->nbr_addr)
	    PUT_EUADDR(htonl(jpl->nbr_addr),cp);
	else
	    printf(" No Upstream Neighbor \n");
    }
    *cp++ = 0;
    cnt = cp;
    *cp++;

    if(holdtime) 
	PUT_HOSTSHORT(holdtime,cp);
    else {
	if(jpl->holdtime)
	    PUT_HOSTSHORT(jpl->holdtime,cp);
	else
	    PUT_HOSTSHORT(0,cp);
    }
    grp_cnt=0;
    for(jp=jpl->jpe;jp;jp=jp->next) {
        PUT_EADDR(htonl(jp->grp_addr),jp->grp_mask,cp);
	grp_cnt++;
	PUT_HOSTSHORT(jp->j_num,cp);
	PUT_HOSTSHORT(jp->p_num,cp);
	for(sp=jp->join;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
	for(sp=jp->prune;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
    }

    *cnt = grp_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_GRAFT);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int pim_gack_send(if_name,src,dst,up_addr,holdtime)
char *if_name;
u_long src,dst,up_addr;
u_short holdtime;
{
    char *cp,*cnt,grp_cnt;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    struct src_list *sp;
    struct jp_entry *jp;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    cp = (char *)(pim+1);

    PUT_EUADDR(htonl(up_addr),cp);    
    *cp++ = 0;
    cnt = cp;
    *cp++;

    PUT_HOSTSHORT(holdtime,cp);

    grp_cnt=0;
    for(jp=jp_entry_list;jp;jp=jp->next) {
        PUT_EADDR(htonl(jp->grp_addr),jp->grp_mask,cp);
	grp_cnt++;
	PUT_HOSTSHORT(jp->j_num,cp);
	PUT_HOSTSHORT(jp->p_num,cp);
	for(sp=jp->join;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
	for(sp=jp->prune;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
    }

    *cnt = grp_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_GRAFT_ACK);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));
    
#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int pim_gack_send2(if_name,src,dst,up_addr,holdtime,list)
char *if_name;
u_long src,dst,up_addr;
u_short holdtime;
int list;
{
    char *cp,*cnt,grp_cnt;
    struct ip *ip;
    struct pim_header *pim;
    struct pim_crp *crp;
    u_long ifaddr;
    struct src_list *sp;
    struct jp_entry *jp;
    struct jp_list *jpl;
    int pim_len,rtn;

    if(if_name) {
        ip = (struct ip *)send_buf;
	pim = (struct pim_header *)(ip+1);
    }
    else {
        pim = (struct pim_header *)send_buf;
    }
    
    for(jpl=jplist;jpl;jpl=jpl->next) {
	if(jpl->id == list) break;
    }
    if(!jpl) return 0;

    cp = (char *)(pim+1);

    if(up_addr)
	PUT_EUADDR(htonl(up_addr),cp);    
    else {
	if(jpl->nbr_addr)
	    PUT_EUADDR(htonl(jpl->nbr_addr),cp);
	else
	    printf(" No Upstream Neighbor \n");
    }
    *cp++ = 0;
    cnt = cp;
    *cp++;

    if(holdtime) 
	PUT_HOSTSHORT(holdtime,cp);
    else {
	if(jpl->holdtime)
	    PUT_HOSTSHORT(jpl->holdtime,cp);
	else
	    PUT_HOSTSHORT(0,cp);
    }
    grp_cnt=0;
    for(jp=jpl->jpe;jp;jp=jp->next) {
        PUT_EADDR(htonl(jp->grp_addr),jp->grp_mask,cp);
	grp_cnt++;
	PUT_HOSTSHORT(jp->j_num,cp);
	PUT_HOSTSHORT(jp->p_num,cp);
	for(sp=jp->join;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
	for(sp=jp->prune;sp;sp=sp->next) {
	    PUT_ESADDR(htonl(sp->addr),htonl(sp->mask),sp->flags,cp);
	}
    }

    *cnt = grp_cnt;
    pim_len = cp - (char *)pim;
    pim->type = (PIM_VERSION << 4 | PIM_GRAFT_ACK);
    pim->cksum = 0;
    pim->cksum = in_cksum((u_short *)pim,pim_len);

    dst = (dst ? htonl(dst) : htonl(ALL_PIM_ROUTERS));

#ifdef IP_HDRINCL
#if 0
    if(if_name) {
        ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
	rtn = pim_send(htonl(ifaddr),htonl(src),ip,htonl(dst),sizeof(struct ip)+pim_len,PIM_TTL);
    }
    else {
        pim_send2(pim,htonl(dst),pim_len,PIM_TTL);
    }
#endif
    PIM_SEND(if_name,src,dst);
#else
    pim_send2(pim,dst,pim_len,PIM_TTL);
#endif

    if(trace_mode)
	pim_trace(NULL,src,dst,pim,pim_len);

    return pim_len;
}

/*
 *
 */
int pim_send(ifaddr,src,ip,dst,iplen,ttl)
u_long ifaddr,src;
struct ip *ip;
u_long dst;
int iplen;
int ttl;
{
    u_char loop;
    int rtn;
    struct sockaddr_in sdst;
    u_char ip_ttl;
    struct in_addr adr;
    int bool ;

#ifdef IP_HDRINCL
    bool = 1;
    if (setsockopt(pim_sock, IPPROTO_IP, IP_HDRINCL,(char *)&bool, sizeof(bool)) < 0)
        perror("setsockopt IP_HDRINCL");

    ip->ip_v = IPVERSION;
    ip->ip_hl = sizeof(struct ip) >> 2;
    ip->ip_tos = 0;
    ip->ip_off = 0;
    ip->ip_p = IPPROTO_PIM;
    ip->ip_ttl = ip_ttl = ttl;
    ip->ip_src.s_addr = src;
    ip->ip_dst.s_addr = dst;
    ip->ip_len = iplen;

#endif /* IP_HDRINCL */

    if(IN_MULTICAST(ntohl(dst))) {
       if (setsockopt(pim_sock,IPPROTO_IP,IP_MULTICAST_TTL, (char *)&ip_ttl,sizeof(ip_ttl)) < 0) 
          perror("setsockopt IP_MULTICAST_TTL");

       adr.s_addr = ifaddr;
       if(setsockopt(pim_sock,IPPROTO_IP,IP_MULTICAST_IF,
		     (char *)&adr, sizeof(adr)) < 0)
	  perror("setsockopt IP_MULTICAST_IF");

       loop=1;
       if(setsockopt(pim_sock,IPPROTO_IP,IP_MULTICAST_LOOP,(char *)&loop,sizeof(loop)) < 0)
	  perror("setsockopt IP_MULTICAST_LOOP");
    }

    bzero(&sdst,sizeof(sdst));
    sdst.sin_family = AF_INET;
    sdst.sin_addr.s_addr = dst;
    if(sendto(pim_sock, (char *)ip, iplen, 0, (struct sockaddr *)&sdst,sizeof(sdst)) < 0) {
        perror("sendto");
    }

    loop=0;
    if(setsockopt(pim_sock,IPPROTO_IP,IP_MULTICAST_LOOP,(char *)&loop,sizeof(loop)) < 0)
	  perror("setsockopt IP_MULTICAST_LOOP");

    return rtn;
}

/*
 *
 */
int pim_send2(pim_pkt,dst,len,ttl)
char *pim_pkt;
u_long dst;
int len;
int ttl;
{
    u_char loop;
    int rtn;
    struct sockaddr_in sdst;
    u_char ip_ttl=ttl;
    struct in_addr adr;
    int bool;

#ifdef IP_HDRINCL

    bool = 0;
    if (setsockopt(pim_sock, IPPROTO_IP, IP_HDRINCL,(char *)&bool, sizeof(bool)) < 0)
        perror("setsockopt IP_HDRINCL");

#endif /* IP_HDRINCL */

    if(IN_MULTICAST(ntohl(dst))) {
       if (setsockopt(pim_sock,IPPROTO_IP,IP_MULTICAST_TTL, (char *)&ip_ttl,sizeof(ip_ttl)) < 0) 
          perror("setsockopt IP_MULTICAST_TTL");

       loop = 1;
       if(setsockopt(pim_sock,IPPROTO_IP,IP_MULTICAST_LOOP,(char *)&loop,sizeof(loop)) < 0)
	  perror("setsockopt IP_MULTICAST_LOOP");
    }

    bzero(&sdst,sizeof(sdst));
    sdst.sin_family = AF_INET;
    sdst.sin_addr.s_addr = dst;
    if(sendto(pim_sock, (char *)pim_pkt, len, 0, (struct sockaddr *)&sdst,sizeof(sdst)) < 0) {
        perror("sendto");
    }

    loop=0;
    if(setsockopt(pim_sock,IPPROTO_IP,IP_MULTICAST_LOOP,(char *)&loop,sizeof(loop)) < 0)
	  perror("setsockopt IP_MULTICAST_LOOP");

    return rtn;
}

/*
 *
 */
void build_grp_list(net,mask,num)
u_long net;
u_long mask;
int num;
{
    u_long start_net;
    u_long i;
    int mask_len,host_len;
    u_long bit;

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

    host_len = 32 - mask_len;
    if(host_len) bit = 0x00000001 << host_len;
    else bit = 0x00000001;

    start_net = net;
    for(i=0;i<num;i++) {
        if((start_net & 0xff000000) == 0xff000000
	|| (start_net & 0x00ff0000) == 0x00ff0000
	|| (start_net & 0x0000ff00) == 0x0000ff00
	|| (start_net & 0x000000ff) == 0x000000ff) {
	    start_net += bit;
	    continue;
	}
#if 0
	add_grp_list(start_net, mask);
#endif
	add_crp_group(0,start_net,mask);
        start_net += bit;
    }

}

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

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

struct pim_nbr *choose_pim_dr(list)
struct pim_nbr *list;
{
    struct pim_nbr *l,*dr;
    
    for(l=dr=list;l!=NULL;l=l->next) {
	if(dr->addr < l->addr)
	    dr = l;
    }

    return dr;
}

struct pim_nbr *find_pim_nbr(list,nbr)
struct pim_nbr *list;
u_long nbr;
{
    struct pim_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_info *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 pim_hello_trace(fp,pim,pim_len)
FILE *fp;
struct pim_header *pim;
int pim_len;
{
    int size;
    struct pim_hello *hello;
    u_short type,len,len2,holdtime,*val,opt_val;
    u_long value,*vp;
    u_char *cp;

    hello = (struct pim_hello *)(pim + 1);
    pim_len -= sizeof(struct pim_header);

    while(pim_len > sizeof(struct pim_hello)) {
	type = ntohs(hello->opt_type);
	len = ntohs(hello->opt_len);

	switch(type) {
	case HELLO_HOLDTIME:
	    if(len != 2) {
		trace2(fp," pim_hello: invalid holdtime len: %d\n",len);
		return;
	    }
	    val = (u_short *)(hello+1);
	    holdtime = ntohs(*val);
	    trace2(fp,"\n\t Option type:holdtime len:%d value:%d\n",len,holdtime);
	    break;
	default:
	    cp = (u_char *)(hello+1);
	    vp = &value;
	    len2 = len;
	    while(len2--) {
		*(vp+len2) = *cp++;
	    }

	    val = (u_short *)(hello+1);
	    opt_val = ntohs(*val);
	    if(type >= 2 && type <= 16) {
		trace2(fp," Option type:unsupported(%d) len:%d value:%d\n",
		      type,len,value);
	    }
	    else {
		trace2(fp," Option type:invalid(%d) len:%d valule:%d\n",
		      type,len,value);
	    }
	}

	size = sizeof(struct pim_hello) + len;
	pim_len -= size;
	hello += size;
    }
    trace2(fp,"\n");
}

/*
 *
 */
void pim_register_trace(fp,pim,pim_len)
FILE *fp;
struct pim_header *pim;
int pim_len;
{
    u_long src,grp;
    int size;
    u_long *reserved = (u_long *)(pim+1);
    u_char *cp = (u_char *)(pim + 1);
    struct ip *ip;

    src = grp = 0;
    if( pim_len > sizeof(struct pim_header) + sizeof(long)) {
	ip = (struct ip *)(reserved + 1);
	grp = ip->ip_dst.s_addr;
	src = ip->ip_src.s_addr;
    }

    if(ntohl(*reserved) & REGISTER_PMBR_BIT)
	trace2(fp,"\t Border bit: On");
    else
	trace2(fp,"\t Border bit: Off");
    if(ntohl(*reserved) & REGISTER_NULL_BIT)
	trace2(fp,"   Null bit: On \n");
    else
	trace2(fp,"   Null bit: Off \n");

    if(src && grp)
	trace2(fp,"\t Encap IP: src %s, dst %s\n",inet_ipstr(src,ipstr1),inet_ipstr(grp,ipstr2));
    else 
	trace2(fp,"\t Encap IP:  (Null)\n");
    trace2(fp,"\n");

}

/*
 *
 */
void pim_register_stop_trace(fp,pim,pim_len)
FILE *fp;
struct pim_header *pim;
int pim_len;
{
    struct in_addr gaddr,saddr,gmask;
    int size;
    struct pim_euaddr eu;
    struct pim_eaddr egrp,esr;
    u_char *cp = (u_char *)(pim + 1);

    GET_EADDR_SOCK((struct pim_eaddr *)&egrp, gaddr, gmask, cp);
    if(egrp.ea_family != 1 || !gaddr.s_addr) {
	printf(" register_stop:bad group address family %d\n",egrp.ea_family);
    }

    GET_EUADDR_SOCK(&eu, saddr, cp);
    if(eu.eu_family != 1 || !saddr.s_addr) {
	printf("bad source address family %d \n",eu.eu_family);
    }

    trace2(fp,"\n\t Group:%s/%d   Source:%s \n\n",
	   inet_ipstr(gaddr.s_addr,ipstr1),egrp.ea_masklen,inet_ipstr(saddr.s_addr,ipstr2));

}

/*
 *
 */
void pim_jp_trace(fp,pim,pim_len)
FILE *fp;
struct pim_header *pim;
int pim_len;
{
    u_char  *cp = (char *)(pim + 1);
    struct in_addr gaddr,gmask,saddr,smask;
    struct in_addr srcaddr;
    char ngroups;
    u_short holdtime,joins,prunes;
    struct pim_eaddr egrp,esrc;
    struct pim_euaddr eu;

    GET_EUADDR_SOCK((struct pim_euaddr *)&eu, srcaddr, cp);
    if(!srcaddr.s_addr) {
	printf("pimsm_jp_recv: recv'd JP from non-local src \n");
	return;
    }
    *cp++;   /* reserved */
    ngroups = *cp++;
    GET_SHORT(holdtime,cp);

    trace2(fp,"\n\t Upstream %s, Ngroups:%d, holdtime:%d \n",
	   inet_ipstr(srcaddr.s_addr,ipstr1),ngroups,holdtime);
    while(ngroups--) {
	GET_EADDR_SOCK((struct pim_eaddr *)&egrp, gaddr, gmask, cp);
	if(egrp.ea_family != 1 || !gaddr.s_addr) {
	    printf("bad address family %d \n", egrp.ea_family);
	    break;
	}
	GET_SHORT(joins, cp);
	GET_SHORT(prunes, cp);

	trace2(fp,"\t   Groups:%s/%d \n",inet_ipstr(gaddr.s_addr,ipstr1),egrp.ea_masklen);
	trace2(fp,"\t     Join's[%d]:\n",joins);
	while(joins--) {
	    GET_EADDR_SOCK((struct pim_eaddr *)&esrc,saddr,smask,cp);
	    if(esrc.ea_family != 1 || !saddr.s_addr) {
		printf("bad address family %d \n",esrc.ea_family);
		break;
	    }
	    trace2(fp,"\t\t%s/%d flags ",inet_ipstr(saddr.s_addr,ipstr1),esrc.ea_masklen);
	    if(esrc.ea_flags & USADDR_S_BIT) trace2(fp,"S");	    
	    if(esrc.ea_flags & USADDR_WC_BIT) trace2(fp,"W");
	    if(esrc.ea_flags & USADDR_RP_BIT) trace2(fp,"R");
	    trace2(fp,"\n");
	}

	trace2(fp,"\t     Prune's[%d]:\n",prunes);
	while(prunes--) {
	    GET_EADDR_SOCK((struct pim_eaddr *)&esrc,saddr,smask,cp);
	    if(esrc.ea_family != 1 || !saddr.s_addr) {
		printf("bad address family %d \n",esrc.ea_family);
		break;
	    }
	    trace2(fp,"\t\t%s/%d flags ",inet_ipstr(saddr.s_addr,ipstr1),esrc.ea_masklen);
	    if(esrc.ea_flags & USADDR_S_BIT) trace2(fp,"S");	    
	    if(esrc.ea_flags & USADDR_WC_BIT) trace2(fp,"W");
	    if(esrc.ea_flags & USADDR_RP_BIT) trace2(fp,"R");
	}
	trace2(fp,"\n");
    }
    trace2(fp,"\n");
}

/*
 *
 */
void pim_bootstrap_trace(fp, pim, pim_len)
FILE *fp;
struct pim_header *pim;
int pim_len;
{
    u_short fragtag;
    u_char hmlen,bsrpri;
    struct in_addr bsraddr;
    struct in_addr gaddr,gmask,saddr;
    u_char           *cp = (char *) (pim + 1);
    struct pim_euaddr       eu;    
    char nrp,nfrag;
    struct pim_eaddr  egrp;
    u_short holdtime;
    u_char pri;

    GET_SHORT(fragtag, cp);
    hmlen = *cp++;
    bsrpri = *cp++;

    GET_EUADDR_SOCK((struct pim_euaddr *)&eu, bsraddr, cp);
    if(eu.eu_family != 1 || !bsraddr.s_addr) {
        printf("\t bad bsraddr family %d\n",eu.eu_family);
	return;
    }

    trace2(fp,"\n\tBSR %s, frag %x, pri %d, host mask len %d\n",
	   inet_atos(bsraddr.s_addr),fragtag,bsrpri,hmlen);

    while((char *)(pim) + pim_len > (char *)cp) {
        GET_EADDR_SOCK(&egrp, gaddr, gmask, cp);
	if(!gaddr.s_addr) {
	    printf("\t bad group addr %x\n",gaddr.s_addr);
	    return;
	}

	nrp = *cp++;
	nfrag = *cp++;
	trace2(fp,"\t  Group %s/%d: frags %d,C-RP's[%d]:\n",
	         inet_atos(gaddr.s_addr),egrp.ea_masklen,nfrag,nrp);
	*cp++;
	*cp++;

	while(nrp--) {

	    GET_EUADDR_SOCK(&eu, saddr, cp);
	    if(eu.eu_family != 1 || !saddr.s_addr) {
	        printf(" bad crpaddr family %d\n",eu.eu_family);
		return;
	    }

	    GET_SHORT(holdtime, cp);
	    pri = *cp++;
	    *cp++;  /* reserved */
	    trace2(fp,"\t\t %s [%d] pri %d\n", inet_atos(saddr.s_addr),holdtime,pri);
	}
    }
    trace2(fp,"\n");
}

/*
 *
 */
void pim_assert_trace(fp,pim,pim_len)
FILE *fp;
struct pim_header *pim;
int pim_len;
{
    struct pim_euaddr eu;
    struct pim_eaddr egrp;
    u_long pref,metric;
    struct in_addr gaddr,gmask,saddr;
    u_char *cp = (char *)(pim + 1);
    int r_bit=0;
    u_char *bit[]={"SPT","RPT"}; 

    GET_EADDR_SOCK(&egrp, gaddr, gmask, cp);
    GET_EUADDR_SOCK(&eu, saddr, cp);
    if(eu.eu_family != 1 || !saddr.s_addr) {
/*	printf("\n\t bad group family %d\n",eu.eu_family);*/
	return;
    }

    GET_NETLONG(pref,cp);
    GET_NETLONG(metric,cp);

    if(ntohl(pref) & ASSERT_RPT_BIT) 
	r_bit = 1;

    pref = ntohl(pref) & ~ASSERT_RPT_BIT;
    trace2(fp,"\n\t %s/%d",inet_ipstr(gaddr.s_addr,ipstr1),egrp.ea_masklen);
    trace2(fp," from %s  metric preference: %d[%s], metric: %d\n",inet_ipstr(saddr.s_addr,ipstr1),pref,bit[r_bit],ntohl(metric));
}

/*
 *
 */
void pim_crp_adv_trace(fp,pim,pim_len)
FILE *fp;
struct pim_header *pim;
int pim_len;
{
    u_char pref, pri;
    u_short holdtime;
    struct pim_euaddr       eu;
    struct pim_eaddr  egrp;
    struct in_addr rpaddr,gaddr,gmask;
    u_char           *cp = (char *) (pim + 1);
    size_t size = (size_t)(pim_len + sizeof(struct pim_header));

    pref = *cp++;
    pri = *cp++;
    GET_SHORT(holdtime,cp);
    GET_EUADDR_SOCK(&eu, rpaddr, cp);
    if(eu.eu_family != 1 || !rpaddr.s_addr) {
        printf("\t crp_adv: bad group family %d\n",eu.eu_family);
	return;
    }

    trace2(fp,"\n\tRP %s, prefix %d, pri %d, holdtime %d\n",inet_atos(rpaddr.s_addr),pref,pri,holdtime);

    trace2(fp,"\t Group: \n");
    if(pref == 0) {
	trace2(fp,"\t   224.0.0.0/4\n ");
	return;
    }

    while((char *)pim + pim_len > (char *)cp) {

        GET_EADDR_SOCK(&egrp, gaddr, gmask, cp);
	if(eu.eu_family != 1 || !gaddr.s_addr) {
	    printf("\t crp_adv: bad group family %d\n",eu.eu_family);
	    return;
	}
	trace2(fp,"\t    %s/%d\n",inet_atos(gaddr.s_addr),egrp.ea_masklen);

    }
    trace2(fp,"\n");

}

/*
 *
 */
void pim_trace(fp,src,dst,pim,pim_len)
FILE *fp;
u_long src;
u_long dst;
struct pim_header *pim;
int pim_len;
{
    struct in_addr addr;
    char *inet_ipstr();
    char vers,type;
    char *types[]={"Hello","Register","Register-Stop","Join/Prune","Bootstrap","Assert","Graft","Graft-Ack","C-RP-Adv"};
    int t;

    if(!trace_mode)
	return;

    ttrace(fp," %s > %s \t",inet_ipstr(src,ipstr1),inet_ipstr(dst,ipstr2));
    vers = pim->type >> 4;
    type = pim->type & 0x0f;

    if(pim->type) {
	trace2(fp,"PIM v%d %s \n",vers,types[type]);
    }

    /* if fp == NULL then trace to terminal */
    if(!fp && trace_mode == TRACE_MODE_PKT) {
/*	trace2(fp,"\n");*/
	return;
    }

    switch(pim->type & 0x0f) {
    case PIM_HELLO: pim_hello_trace(fp,pim,pim_len);
	break;
    case PIM_REGISTER: pim_register_trace(fp,pim,pim_len);
	break;
    case PIM_REGISTER_STOP: pim_register_stop_trace(fp,pim,pim_len);
	break;
    case PIM_JOIN_PRUNE:
    case PIM_GRAFT: 
    case PIM_GRAFT_ACK: pim_jp_trace(fp,pim,pim_len);
	break;
    case PIM_BOOTSTRAP: pim_bootstrap_trace(fp,pim,pim_len);
	break;
    case PIM_ASSERT: pim_assert_trace(fp,pim,pim_len);
	break;
    case PIM_CRP_ADV: pim_crp_adv_trace(fp,pim,pim_len);
	break;
    default : break;
    }

}

/*
 *
 */
void pim_hello_recv(src,dst,pim,pim_len)
u_long src,dst;
struct pim_header *pim;
int pim_len;
{
    u_long nbr;
    int len;
    struct if_info *ifp;
    struct vif_info *vif;
    int i,new_nbr;
    struct pim_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_pim_nbr(nbr_list,src);
	if(!nbrs) {
	    nbrs = (struct pim_nbr *)malloc(sizeof(struct pim_nbr));
	    add_vif_nbr_list(i,nbrs);
	    new_nbr = 1;
	}
    }
    else {
	nbrs = (struct pim_nbr *)malloc(sizeof(struct pim_nbr));
	add_vif_nbr_list(i,nbrs);
	new_nbr = 1;
    }

    nbrs->addr = src;
    nbrs->state = 0;

    if(pim_recv_func[PIM_HELLO].recv_routine)
	pim_recv_func[PIM_HELLO].recv_routine(i,src,dst,nbrs,pim,pim_len);
    else
	printf(" Function not Register \n");
}

/*
 *
 */
void pim_bsr_recv(src,dst,pim,pim_len)
u_long src,dst;
struct pim_header *pim;
int pim_len;
{
    u_long nbr;
    int len;
    u_long *dvmrp;
    struct if_info *ifp;
    struct vif_info *vif;
    int i,new_nbr;

    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;

    if(pim_recv_func[PIM_BOOTSTRAP].recv_routine)
	pim_recv_func[PIM_BOOTSTRAP].recv_routine(i,src,dst,pim,pim_len);
    else
	printf(" Function not Register \n");
}

/*
 *
 */
void pim_jp_recv(src,dst,pim,pim_len)
u_long src,dst;
struct pim_header *pim;
int pim_len;
{
    u_long nbr;
    int len;
    u_long *dvmrp;
    struct if_info *ifp;
    struct vif_info *vif;
    int i,new_nbr;

    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;

    if(pim_recv_func[PIM_JOIN_PRUNE].recv_routine)
	pim_recv_func[PIM_JOIN_PRUNE].recv_routine(i,src,dst,pim,pim_len);
    else
	printf(" Function not Register \n");
}

/*
 *
 */
void pim_crp_adv_recv(src,dst,pim,pim_len)
u_long src,dst;
struct pim_header *pim;
int pim_len;
{
    u_long nbr;
    int len;
    u_long *dvmrp;
    struct if_info *ifp;
    struct vif_info *vif;
    int i,new_nbr;

    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;

    if(pim_recv_func[PIM_CRP_ADV].recv_routine)
	pim_recv_func[PIM_CRP_ADV].recv_routine(i,src,dst,pim,pim_len);
    else
	printf(" Function not Register \n");
}

/*
 *
 */
void pim_recv(recvlen)
int recvlen;
{
    u_long  src,dst,grp;
    struct ip *ip;
    struct pim_header *pim;
    struct igmpmsg *igmpctl;
    int data_len, hdr_len, pim_len;

    ip = (struct ip *)recv_buf;
    src = ip->ip_src.s_addr;
    dst = ip->ip_dst.s_addr;

    if(ip->ip_p == 0) {
	if(src == 0 || dst == 0) 
	    printf(" Illegal Kernel Request Cache \n");
	else {
	    igmpctl = (struct igmpmsg *)(recv_buf + hdr_len);
	    switch(igmpctl->im_msgtype) {
	    case IGMPMSG_NOCACHE:
		if(igmp_sg_func.recv_routine)
		    igmp_sg_func.recv_routine(ntohl(src),ntohl(dst));
		break;
	    case IGMPMSG_WRONGVIF:
		printf(" Packet receiving from oif\n");
		break;
	    default:
		printf("Uknown upcall type \n");
		break;
	    }
	}
	return;
    }
    else if(ip->ip_p == IPPROTO_IGMP) 
	return;

    hdr_len = ip->ip_hl << 2;
    data_len = ip->ip_len;
    if(hdr_len + data_len != recvlen) {
	return;
    }

    pim = (struct pim_header *)(recv_buf + hdr_len);
    pim_len = data_len;
    if(pim_len < 0) {
	return;
    }

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

    pim_trace(NULL,src,dst,pim,pim_len);


    if(pim_comp_func) {
	pim_comp_func(src, dst, pim->type & 0x0f, pim, pim_len);
    }

    switch(pim->type & 0x0f) {
    case PIM_HELLO : pim_hello_recv(ntohl(src),htonl(dst),pim,pim_len);
	break;
    case PIM_REGISTER: 
	break;
    case PIM_REGISTER_STOP: 
	break;
    case PIM_JOIN_PRUNE: pim_jp_recv(ntohl(src),htonl(dst),pim,pim_len);
	break;
    case PIM_ASSERT:
	break;
    case PIM_BOOTSTRAP : pim_bsr_recv(ntohl(src),htonl(dst),pim,pim_len);
	break;
    case PIM_GRAFT:
    case PIM_GRAFT_ACK:
	break;
    case PIM_CRP_ADV : pim_crp_adv_recv(ntohl(src),htonl(dst),pim,pim_len);
	break;
    default : break;
    }

}

void vif_dump()
{
    struct vif_info *vif;
    int i;
    struct pim_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 ",inetaddr_to_char(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",inetaddr_to_char(htonl(nbr_list->addr)));
	}
    }

}
