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

#include "defs.h"
#include "pim_def.h"
#include "./config2/config.h"
#include <netinet/ip_icmp.h>

#define FILE_NAME_SIZE 256
#define IP_DATA_SIZE 64

#define FILE_MODE         0
#define INTERACTIVE_MODE  1

#define CNF_QUIT  3

extern int pim_sock;
extern int igmp_sock;
extern char *recv_buf;
extern int insock;
extern int trace_mode;

void wait_command();
void wait_timeout();
char *timer_init();
void parse_file();
void print_usage();
void encap_ip_packet();

char usage[] = "Usage: pimnbr [-f conf]\n";
char version[]="PIM tool (pimnbr) -- Version 1.0 Beta1";
char copyright[]="All Right Reserved, Copyright(c) 1998, Hitachi,Ltd";

char *if_name;
FILE *fp;
char *config;
CONFIG *configp;
CONF_PARAGET gettbl;
int cnt;  /* config parameter counter */

char *wait_timer;
int wait_flag=0;

int mode;
int debug = 0;

main(argc, argv)
int argc;
char **argv;
{
    char opt;
    extern char *optarg;
    fd_set read_fd,tmp_fd,pim_fd,igmp_fd;
    int recv_len;
    int dummy;
    int nfd;
    struct timeval  t;
    int rtn;
    int n;
    char *log_file;
    int fd_val, fd_wid;

    if_name=log_file=NULL;
    dummy = rtn = 0;
    
    while((opt = getopt(argc,argv,"f:l:d:h")) != -1) {
        switch(opt) {
	case 'l' : log_file = malloc(FILE_NAME_SIZE);
	           strcpy(log_file,optarg);
	           break;
	case 'f' : configp=init_config(optarg);
	           mode = FILE_MODE;
	           rtn = 1;
	           break;
	case 'd' : debug = atoi(optarg);
	           break;
	case 'h':  fprintf(stderr,usage);
	           exit(1);
	default :  fprintf(stderr,usage);
	           exit(1);
	}
    }

    printf("\n %s \n\n",version);
/*    printf(" %s \n\n",copyright);*/

    if(!configp) {
	configp = init_config(NULL);
	mode = INTERACTIVE_MODE;
	printf(" Running interactive mode, Enter 'help' or command:\n");
	printf(" pimnbr> ");
	fflush(stdout);
    }

    interface_init();
    igmp_init();
    pim_init();

    multicast_init();
    init_assert();
    router_init(log_file);

    time_init();
    wait_timer = timer_init(0,NULL,wait_timeout);

    FD_ZERO(&read_fd);
    FD_SET(pim_sock, &read_fd);
    FD_SET(pim_sock, &pim_fd);
/*    fd_wid = getdtablesize();*/
    if(pim_sock >= insock) fd_wid = pim_sock+1;
    else fd_wid = insock+1;

    FD_SET(igmp_sock,&read_fd);
    if(igmp_sock >= fd_wid) fd_wid = igmp_sock+1;

    t.tv_sec = 0;
    t.tv_usec = 10000;

    for(;;) {
        timer_check();
	FD_ZERO(&read_fd);
	FD_SET(pim_sock, &read_fd);
	FD_SET(igmp_sock, &read_fd);
	if(mode == INTERACTIVE_MODE)
	    FD_SET(insock, &read_fd);

/*	memcpy((char *)&tmp_fd, (char *)&read_fd, sizeof(read_fd));*/
	
	if((n=select(fd_wid,&read_fd,NULL,NULL,&t))<0) {
	    perror("select");
	    continue;
	}

	for(fd_val=0; n--; fd_val++) {

	    while(!FD_ISSET(fd_val, &read_fd))
		fd_val++;

	    /*	if(FD_ISSET(igmp_sock,&read_fd)) {*/
/*	    if(FD_ISSET(fd_val, &pim_fd)) {*/
	    if(fd_val == pim_sock) {
		recv_len = recvfrom(pim_sock,recv_buf,1024,0,NULL,&dummy);
		pim_recv(recv_len);
	    }
	    else if(fd_val == igmp_sock) {
		recv_len = recvfrom(igmp_sock,recv_buf,1024,0,NULL,&dummy);
		pim_recv(recv_len);
	    }

	    if(mode == INTERACTIVE_MODE && fd_val == insock) {
		rtn = do_command();
		if(rtn == CNF_QUIT) break;
		printf(" pimnbr> ");
		fflush(stdout);
	    }
	}

	if(mode == FILE_MODE) {
	    if(!wait_flag) {
		rtn = do_command();
		if(rtn == CNF_QUIT) break;
		else if(rtn == CNF_EOF) {
                    end_config(configp);
                    configp = init_config(NULL);
                    mode = INTERACTIVE_MODE;
                    printf(" \n Transit to interactive mode, Enter 'help' or command:\n");
                    printf(" pimnbr> ");
                    fflush(stdout);
		}
	    }
	}
	else
	    if(rtn == CNF_QUIT) break;

    }

    end_config(configp);

    multicast_term();
}

typedef enum {
    C_ROUTER_LIST=1,
    C_NETWORK_LIST,
    C_RPSET_LIST,
    C_BSR_LIST,
    C_CRP_LIST,
    C_JP_LIST,
    C_RPF,
    C_CONF_FILE,
    C_TRACE_FILE,
    C_ROUTER,
    C_NETWORK,
    C_MEMBER,
    C_HELLO,
    C_REGISTER,
    C_REGISTER_STOP,
    C_JOIN_PRUNE,
    C_ASSERT,
    C_BOOTSTRAP,
    C_GRAFT,
    C_GACK,
    C_CRP_ADV,
    C_WAIT,
    C_DUMP,
    C_MFC,
    C_DEBUG,
    C_NODEBUG,
    C_QUIT,
    C_HELP
} COMMAND;

typedef enum {
    RC_UP=1,
    RC_DOWN,
    RC_ADV,
    RC_NOADV
} ROUTER_COMMAND;

typedef enum {
    NC_ADD=1,
    NC_DELETE,
    NC_CHANGE
} NETWORK_COMMAND;

typedef enum {
    PC_SEND=1,
    PC_RECV
} PKT_COMMAND;

typedef enum {
    P_ADDR=1,
    P_NET,
    P_BSRLIST,
    P_CRPLIST,
    P_JPLIST,
    P_HELLOINTERVAL,
    P_FILE,
    P_ROUTER,
    P_SRC,
    P_DST,
    P_GROUP,
    P_TIME,
    P_NBR,
    P_TIMEOUT,
    P_LIST,
    P_RP,
    P_RPSET,
    P_HELLO_OPT,
    P_BSR,
    P_CRP,
    P_PRIORITY,
    P_PERIOD,
    P_RPF,
    P_ENCAPIP,
    P_N_BIT,
    P_B_BIT,
    P_NB_BIT,
    P_ENTRY,
    P_JOIN,
    P_PRUNE,
    P_RP_BIT,
    P_WC_BIT,
    P_S_BIT,
    P_PREF,
    P_METRIC,
    P_TRACEFILE,
    P_BOOTSTRAP,
    P_CRPADV,
    P_JOIN_PRUNE
} RT_PARAM;

typedef enum {
    DBG_DETAIL=1,
    DBG_TTY
} DBG_COMMAND;

struct token {
    int type;
    char *name;
};

struct token cmd_token[]= {
    {C_ROUTER_LIST,"router-list"},
    {C_NETWORK_LIST,"network-list"},
    {C_RPSET_LIST,"rpset-list"},
    {C_BSR_LIST, "bsr-list"},
    {C_CRP_LIST,"crp-list"},
    {C_JP_LIST,     "jp-list"},
    {C_RPF,  "rpf"},
    {C_CONF_FILE,  "config-file"},
    {C_TRACE_FILE, "trace-file"},
    {C_ROUTER,     "router"},
    {C_NETWORK,    "network"},
    {C_MEMBER,     "member"},
    {C_HELLO,      "hello"},
    {C_REGISTER,     "register"},
    {C_REGISTER_STOP,      "register-stop"},
    {C_JOIN_PRUNE,   "join-prune"},
    {C_ASSERT,     "assert"},
    {C_BOOTSTRAP,    "bootstrap"},
    {C_GRAFT,        "graft"},
    {C_GACK,       "gack"},
    {C_CRP_ADV,    "crpadv"},
    {C_WAIT,       "wait"},
    {C_DUMP,       "dump"},
    {C_MFC,        "mfc"},
    {C_DEBUG,      "debug"},
    {C_NODEBUG,    "nodebug"},
    {C_QUIT,       "quit"},
    {C_HELP,       "help"}
};

struct token rtr_cmd_token[]={
    {RC_UP, "up"},
    {RC_DOWN, "down"},
    {RC_ADV, "adv"},
    {RC_NOADV,"no-adv"}
};

struct token net_cmd_token[]={
    {NC_ADD,  "add"},
    {NC_DELETE, "delete"},
    {NC_CHANGE, "change"}
};

struct token pkt_cmd_token[]={
    {PC_SEND, "send"},
    {PC_RECV, "recv"}
};

struct token param_token[]={
    {P_ADDR, "addr"},
    {P_NET, "network"},
    {P_BSRLIST,  "bsr-list"},
    {P_CRPLIST,  "crp-list"},
    {P_HELLOINTERVAL, "hello-interval"},
    {P_FILE, "file"},
    {P_ROUTER, "router"},
    {P_SRC, "src"},
    {P_DST, "dst"},
    {P_GROUP, "group"},
    {P_TIME, "holdtime"},
    {P_NBR, "upnbr"},
    {P_TIMEOUT, "timeout"},
    {P_LIST, "list"},
    {P_RP, "rp"},
    {P_RPSET, "rpset"},
    {P_HELLO_OPT, "hello-option"},
    {P_BSR, "bsr"},
    {P_CRP, "crp"},
    {P_PRIORITY, "priority"},
    {P_PERIOD, "period"},
    {P_RPF, "rpf"},
    {P_ENCAPIP, "encapip"},
    {P_N_BIT, "N"},
    {P_B_BIT, "B"},
    {P_NB_BIT, "NB"},
    {P_ENTRY, "entry"},
    {P_JPLIST,"jp-list"},
    {P_JOIN, "join"},
    {P_PRUNE, "prune"},
    {P_RP_BIT, "RP"},
    {P_WC_BIT, "WC"},
    {P_S_BIT, "S"},
    {P_PREF, "pref"},
    {P_METRIC, "metric"},
    {P_TRACEFILE,"tracefile"},
    {P_BOOTSTRAP,"bootstrap"},
    {P_CRPADV,"crpadv"},
    {P_JOIN_PRUNE, "join-prune"}
};

struct token dbg_cmd_token[]={
    {DBG_DETAIL, "detail"},
    {DBG_TTY, "tty"}
};

#define TRACE_COMMAND(tbl)  \
{ \
      int c; \
      trace(stderr," %s ",tbl.command); \
      for(c=0;c<tbl.paramcount;c++) trace(stderr,"%s ",tbl.paramtbl[c]); \
      trace(stderr,"\n"); \
}

#define PARAMTBL(cnt)  gettbl.paramtbl[cnt++]

log_command(cmd,tbl)
int cmd;
CONF_PARAGET *tbl;
{
      int c;
      char log_buf[256], temp[32];

      if(cmd == C_QUIT) {
	  strcpy(log_buf,"CMD quit \n");
      }
      else {
	  sprintf(log_buf,"CMD %s ",tbl->command); 
	  for(c=0;c<tbl->paramcount;c++) {
	      sprintf(temp,"%s ",tbl->paramtbl[c]);
	      strcat(log_buf,temp);
	  }
      }
      log(LOG_INFO,0," %s\n",log_buf); 
}

int token_type(list,token)
struct token *list;
char *token;
{
    struct token *p;
    
    for(p=list;p->name != NULL; p++) {
	if(!strcmp(token,p->name)) {
	    return p->type;
	}
    }

    return 0;
}

int do_packet_send_command(cmd)
char cmd;
{
    char c,buf[256],*bufp,if_name[8];
    char param;
    u_long addr,id,interval;
    int rt;
    u_long net,mask;
    u_short ver,time,pri,htype,len;
    int val;
    u_short holdtime;
    u_long sg_src,sg_grp;
    u_long src,dst;
    FILE *fp;
    u_long metric,pref;
    int type;
    char ippkt[IP_DATA_SIZE],*ip=NULL;
    u_long n_bit,b_bit;
    int r_bit;
    int list;

    strcpy(if_name,PARAMTBL(cnt));
    src = dst = 0;
    switch(cmd) {
    case C_HELLO: 
	type = HELLO_HOLDTIME;
	len = HELLO_HOLDTIME_LEN;
	val = DEFAULT_HOLDTIME;
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(P_SRC == param)
		src = inet_stoa(PARAMTBL(cnt));
	    else if(P_DST == param)
		dst = inet_stoa(PARAMTBL(cnt));
	    else if(P_HELLO_OPT == param) {
		type = atoi(PARAMTBL(cnt));
		len = atoi(PARAMTBL(cnt));
		val = atoi(PARAMTBL(cnt));
	    }
	}
	pim_hello_send(if_name,src,dst,type,len,val);
	break;
    case C_REGISTER:
	n_bit = b_bit = 0;
	ip = NULL;
	sg_src = sg_grp = 0;
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(P_SRC == param)
		src = inet_stoa(PARAMTBL(cnt));
	    else if(P_DST == param) 
		dst = inet_stoa(PARAMTBL(cnt));
	    else if(P_ENCAPIP == param) {
		sg_src = inet_stoa(PARAMTBL(cnt));
		sg_grp = inet_stoa(PARAMTBL(cnt));
	    }
	    else if(P_N_BIT == param) n_bit = REGISTER_NULL_BIT;
	    else if(P_B_BIT == param) b_bit = REGISTER_PMBR_BIT;
	    else if(P_NB_BIT == param)  {
		n_bit = REGISTER_NULL_BIT;
		b_bit = REGISTER_PMBR_BIT;
	    }
	    else {
		printf("Unkown parameter at 'register' command\n");
	    }
	}
	if(!n_bit) {
	    if(sg_src && sg_grp) {
	        encap_ip_packet(ippkt,sg_src,sg_grp);
		ip = ippkt;
	    }
	}
	
	if(dst)
	    pimsm_register_send(if_name,src,dst,ip,n_bit,b_bit);
	else
	    printf(" Not set dest address\n");
	break;
    case C_REGISTER_STOP:
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(param == P_SRC) 
		src = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_DST)
		dst = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_ENTRY) {
		sg_src = inet_stoa(PARAMTBL(cnt));
		prefix_parse(PARAMTBL(cnt),&sg_grp,&mask);
	    }
	    else {
		printf("Unknown Parameter at 'register-stop' command");
	    }
	}
	if(dst)
	    pimsm_register_stop_send(if_name,src,dst,sg_src,sg_grp,mask);
	else
	    printf(" Not set dest address\n");
	break;
    case C_JOIN_PRUNE:
    case C_GRAFT:
    case C_GACK:
	addr = 0;
	holdtime = 0;
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(param == P_SRC)
		src = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_DST)
		dst = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_NBR)
		addr = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_TIME) 
		holdtime = (u_short)atoi(PARAMTBL(cnt));
	    else if(param == P_JPLIST)
		list = atoi(PARAMTBL(cnt));
	}
	if(cmd == C_JOIN_PRUNE) {
	    pimsm_jp_send2(if_name,src,dst,addr,holdtime,list);
	}
	else if(cmd == C_GRAFT) {
	    pim_graft_send2(if_name,src,dst,addr,holdtime,list);
	}
	else if(cmd == C_GACK) {
	    pim_gack_send2(if_name,src,dst,addr,holdtime,list);
	}
	break;
    case C_BOOTSTRAP:
	src = dst = addr = 0;
	pri = 0;
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(param == P_SRC)
		src = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_DST)
		dst = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_ADDR)
		addr = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_PRIORITY) 
		pri = atoi(PARAMTBL(cnt));
	    else if(param == P_RPSET)
		list = atoi(PARAMTBL(cnt));
	}
	pimsm_bsr_send2(if_name,src,dst,addr,pri,list);
	break;
    case C_ASSERT:
	r_bit = 0;
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(param == P_SRC)
		src = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_DST)
		dst = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_PREF)
		pref = atol(PARAMTBL(cnt));
	    else if(param == P_METRIC) 
		metric = atol(PARAMTBL(cnt));
	    else if(param == P_ENTRY) {
		sg_src = inet_stoa(PARAMTBL(cnt));
		prefix_parse(PARAMTBL(cnt),&sg_grp,&mask);
	    }
	    else if(param == P_RP_BIT) {
		r_bit = 1;
	    }
	    else {
		printf(" Unknonw parameter at '%s' command\n",gettbl.command);
	    }
	}
	pimsm_assert_send(if_name,src,dst,sg_src,sg_grp,mask,pref,metric,r_bit);
	break;
    case C_CRP_ADV:
	holdtime = PIM_RP_HOLDTIME;
	pri = 0;
	addr = 0;
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(param == P_SRC)
		src = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_DST)
		dst = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_ADDR)
		addr = inet_stoa(PARAMTBL(cnt));
	    else if(param == P_PRIORITY) 
		pri = atoi(PARAMTBL(cnt));
	    else if(param == P_TIME)
		holdtime = (u_short)atoi(PARAMTBL(cnt));
	    else if(param == P_CRPLIST) {
		list = atoi(PARAMTBL(cnt));
	    }
	    else {
		printf(" Unknonw parameter at '%s' command\n",gettbl.command);
	    }
	}
	if(!dst) {
	    printf(" Not set dest address\n");
	    return;
	}
	pimsm_crp_send2(if_name,src,dst,addr,holdtime,(u_char)pri,list);
	break;
    default:
	break;
    }
}

int do_packet_recv_command(cmd)
char cmd;
{
    char c,buf[256],*bufp,if_name[8];
    char param;
    u_long addr,id,interval;
    int rt;
    u_long net,mask;
    u_short ver,time,pri,htype,len;
    int lifetime,val;
    u_long sg_src,sg_grp;
    u_long src,dst;
    FILE *fp;
    int metric;
    int type;
    char ippkt[IP_DATA_SIZE],*ip=NULL;
    u_long n_bit,b_bit;

    strcpy(if_name,PARAMTBL(cnt));
    src = dst = 0;
    for(;cnt<gettbl.paramcount;) {
	param = token_type(param_token,gettbl.paramtbl[cnt++]);
	if(param == P_TIMEOUT) {
	    interval = atoi(gettbl.paramtbl[cnt++]);
	    wait_command(interval);
	}
    }
    
    switch(cmd) {
    case C_HELLO:         type = PIM_HELLO;	break;
    case C_REGISTER:      type = PIM_REGISTER;	break;
    case C_REGISTER_STOP: type = PIM_REGISTER_STOP;	break;
    case C_JOIN_PRUNE:    type = PIM_JOIN_PRUNE;	break;
    case C_ASSERT:        type = PIM_ASSERT;	break;
    case C_BOOTSTRAP:     type = PIM_BOOTSTRAP;	break;
    case C_GRAFT:         type = PIM_GRAFT;	break;
    case C_GACK:          type = PIM_GRAFT_ACK;	break;
    case C_CRP_ADV:       type = PIM_CRP_ADV;	break;
    default:
	break;
    }

    comp_packet_recv(type,interval);

}
	

int do_command()
{
    char c,buf[256],*bufp,if_name[8];
    char cmd,sub_cmd,param;
    u_long addr,id,interval;
    int rt;
    int list;
    u_long net,mask;
    u_short ver,time,pri,htype,len;
    int lifetime,val;
    u_long sg_src,sg_grp;
    u_long src,dst;
    FILE *fp;
    int metric;
    int type;
    char *pim_packet[]={"hello","register","register_stop","join_prune",
			    "bootstrap","assert","graft","graft_ack",
		            "crp_adv"};
    char ippkt[IP_DATA_SIZE],*ip=NULL;
    u_long n_bit,b_bit;
    u_char bits;

    if((rt = get_config_command(configp,&gettbl))<0) {
	printf(" Config read error !\n");
	return 0;
    }

    if(rt == CNF_EOF) {
	return CNF_EOF;
    }
    else {
	cmd = token_type(cmd_token,gettbl.command);
	if(mode == FILE_MODE)
	    TRACE_COMMAND(gettbl);
    }

    log_command(cmd,&gettbl);
    switch(cmd) {
    case C_ROUTER_LIST : 
	cnt = 0;
	id = atol(gettbl.paramtbl[cnt++]);
	param = token_type(param_token,gettbl.paramtbl[cnt++]);
	if(P_ADDR == param) {
	    addr = inet_stoa(gettbl.paramtbl[cnt++]);
	    interval = PIM_HELLO_INTERVAL;
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,gettbl.paramtbl[cnt++]);
		if(P_HELLOINTERVAL == param) {
		    interval = atoi(gettbl.paramtbl[cnt++]);
		}
	    }
	    router_add(id,addr,interval);
	    pim_if_init(addr);
	}
	else if(P_BSRLIST == param) {
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,gettbl.paramtbl[cnt++]);
		if(P_ADDR == param) {
		    addr = inet_stoa(gettbl.paramtbl[cnt++]);
		    pri = atoi(gettbl.paramtbl[cnt++]);
		}
		else if(P_RPSET == param) {
		    id = atoi(gettbl.paramtbl[cnt++]);
		}
		router_add_bsr(addr,pri,id);
	    }
	}
	else if(P_CRPLIST == param) {
	    /* TODO */
	    printf(" This function is not implemented yet \n");
	}
	else if(P_HELLOINTERVAL == param) {
	    interval = atoi(gettbl.paramtbl[cnt++]);
	    router_update_hellointerval(id,interval);
	}
	else if(P_HELLO_OPT == param) {
	    htype = (u_short)atoi(gettbl.paramtbl[cnt++]);
	    len = (u_short)atoi(gettbl.paramtbl[cnt++]);
	    val = atoi(gettbl.paramtbl[cnt++]);
	    router_update_hello(id,htype,len,val);
	}
	else if(P_BSR == param) {
	    pri = 1;
	    interval = PIM_BOOTSTRAP_PERIOD;
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,gettbl.paramtbl[cnt++]);
		if(P_PRIORITY == param) {
		    pri = atoi(gettbl.paramtbl[cnt++]);
		}
		else if(P_PERIOD == param) {
		    interval = atoi(gettbl.paramtbl[cnt++]);
		}
	    }
	    router_bsr(id,pri,interval);
	}
	else if(P_CRP == param) {
	    /* TODO */
	    printf(" This function is not implemented yet\n");
	}
	else if(P_RPF == param) {
	    prefix_parse(gettbl.paramtbl[cnt++],&net,&mask);
	    addr = inet_stoa(gettbl.paramtbl[cnt++]);
	    add_src_net(net,mask,addr);
	}
	else if(P_TRACEFILE == param) {
	    router_trace(id,gettbl.paramtbl[cnt++]);
	}
	else {
	    printf("Unknown Parameter at 'router-list' command\n");
	}
	break;
    case C_NETWORK_LIST:
	break;
    case C_RPSET_LIST:
	cnt = 0;
	id = atol(gettbl.paramtbl[cnt++]);
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,gettbl.paramtbl[cnt++]);
	    if(param == P_RP) {
		addr = char_to_inetaddr(gettbl.paramtbl[cnt++]);
		time = (u_short)atoi(gettbl.paramtbl[cnt++]);
		pri = (u_short)atoi(gettbl.paramtbl[cnt++]);
		add_rpset_list2(id,net,mask,addr,time,pri);
	    }
	    else if(param == P_GROUP) {
		prefix_parse(gettbl.paramtbl[cnt++],&net,&mask);
		/* add_grp_list(net,mask); */
		add_rpset_group(id,net,mask);
	    }
	    else {
		printf(" Unknown Parameter at 'rpset-list' command \n");
	    }
	}
	break;
    case C_BSR_LIST:
	cnt = 0;
	id = atoi(gettbl.paramtbl[cnt++]);
	addr = 0;
	pri = 0;
	param = token_type(param_token,gettbl.paramtbl[cnt++]);
	if(param == P_ADDR) {
	    addr = inet_stoa(gettbl.paramtbl[cnt++]);
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,gettbl.paramtbl[cnt++]);
		if(param == P_PRIORITY) 
		    pri = atoi(gettbl.paramtbl[cnt++]);
		else
		    printf(" Unknown Parameter at 'bsr-list' command\n");
	    }
	    add_bsr_list(id,addr,pri);
	}
	else if(param == P_RPSET) {
	    list = atoi(gettbl.paramtbl[cnt++]);
	    add_bsr_rpset(id,list);
	}
	else {
	    printf(" Unknown Parameter at 'bsr-list' command \n");
	}
	break;
    case C_CRP_LIST:
	cnt = 0;
	id = atol(gettbl.paramtbl[cnt++]);
	param = token_type(param_token,gettbl.paramtbl[cnt++]);
	if(param == P_ADDR) {
	    addr = char_to_inetaddr(gettbl.paramtbl[cnt++]);
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,gettbl.paramtbl[cnt++]);
		if(param == P_TIME)
		    time = atoi(gettbl.paramtbl[cnt++]);
		else if(param == P_PRIORITY)
		    pri = atoi(gettbl.paramtbl[cnt++]);
	    }
	    add_crp_list(id,addr,time,pri);
	}
	else if(param == P_GROUP) {
	    prefix_parse(gettbl.paramtbl[cnt++],&net,&mask);
	    add_crp_group(id,net,mask);
	}
	else {
	    printf(" Unknown Parameter at 'crp-list' command \n");
	}
	break;
    case C_JP_LIST:
	cnt=0;
	bits=0;
	id = atol(gettbl.paramtbl[cnt++]);
	param = token_type(param_token,gettbl.paramtbl[cnt++]); 
	if(param == P_NBR) {
	    addr = char_to_inetaddr(gettbl.paramtbl[cnt++]);
	    param = token_type(param_token,gettbl.paramtbl[cnt++]);
	    lifetime = atoi(gettbl.paramtbl[cnt++]);
	    add_jp_list_nbr(id, addr, lifetime);
	}
	else if(param == P_GROUP) {
	    int jp;

	    prefix_parse(gettbl.paramtbl[cnt++],&sg_grp,&mask); 
	    add_jp_list(id,sg_grp,mask);
	    jp = token_type(param_token,gettbl.paramtbl[cnt++]);
	    prefix_parse(gettbl.paramtbl[cnt++],&sg_src,&mask);
	    for(;cnt<gettbl.paramcount;) {
		type = token_type(param_token,gettbl.paramtbl[cnt++]);
		if(type == P_RP_BIT) bits |= USADDR_RP_BIT;
		else if(type == P_WC_BIT) bits |= USADDR_WC_BIT;
		else if(type == P_S_BIT) bits |= USADDR_S_BIT;
	    }
	    if(P_JOIN == jp)
		add_jp_join_list2(id,sg_grp,sg_src,mask,bits);
	    else if(P_PRUNE == jp)
		add_jp_prune_list2(id,sg_grp,sg_src,mask,bits);
	}
	break;
    case C_RPF:
	cnt = 0;
	prefix_parse(gettbl.paramtbl[cnt++],&net,&mask);
	addr = char_to_inetaddr(gettbl.paramtbl[cnt++]);
	add_src_net(net,mask,addr);
	break;
    case C_ROUTER :
	cnt  = 0;
	sub_cmd = token_type(rtr_cmd_token,gettbl.paramtbl[cnt++]);
	id = atol(gettbl.paramtbl[cnt++]);
	if(sub_cmd == RC_UP) {
	    router_up(id);
	}
	else if(sub_cmd == RC_DOWN) {
	    router_down(id);
	}
	else if(sub_cmd == RC_ADV) {
	    int adv_type;

	    val = 0;
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,gettbl.paramtbl[cnt++]);
		if(param == P_BOOTSTRAP || param == P_CRPADV || param == P_JOIN_PRUNE) {
		    list = atoi(gettbl.paramtbl[cnt++]);
		    adv_type = param;
		}
		else if(param == P_PERIOD) {
		    val = atoi(gettbl.paramtbl[cnt++]);
		}
	    }
	    if(adv_type == P_BOOTSTRAP) router_adv_bsr(id,list,val);
	    else if(adv_type == P_CRPADV) router_adv_bsr(id,list,val);
	    else if(adv_type == P_JOIN_PRUNE) router_adv_jp(id,list,val);
	    else printf(" Unknown PIM packet type \n");
	}
	else if(sub_cmd == RC_NOADV) {
	    printf(" >>This command is not implemented yet\n");
	}
	else {
	    printf(" >>Unknown operation '%s' \n",gettbl.paramtbl[0]);
	}
	break;
    case C_NETWORK:
	cnt = 0;
	sub_cmd = token_type(rtr_cmd_token,gettbl.paramtbl[cnt++]);
	id = atoi(gettbl.paramtbl[cnt++]);
	param = token_type(param_token,gettbl.paramtbl[cnt++]);
	if(param == P_ADDR) {
	    prefix_parse(gettbl.paramtbl[cnt++],&net,&mask);
	}
	else if(param == P_FILE) {
	    /*  */
	}
	if(sub_cmd == NC_ADD)
	    add_network(id,net,mask,atoi(gettbl.paramtbl[cnt++]));
	else if(sub_cmd == NC_DELETE)
	    delete_network(id,net,mask,atoi(gettbl.paramtbl[cnt++]));
	else if(sub_cmd == NC_CHANGE)
	    change_network(id,net,mask,atoi(gettbl.paramtbl[cnt++]));
#if 0
	router_update_route(id);
#endif 0
	break;
    case C_MEMBER:
	cnt=0;
	sub_cmd = token_type(rtr_cmd_token,gettbl.paramtbl[cnt++]);
	id = atoi(gettbl.paramtbl[cnt++]);
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,gettbl.paramtbl[cnt++]);
	    if(param == P_SRC) 
		sg_src = char_to_inetaddr(gettbl.paramtbl[cnt++]);
	    else if(param == P_GROUP)
		sg_grp = char_to_inetaddr(gettbl.paramtbl[cnt++]);
	}
	if(sub_cmd==NC_ADD) 
	    member_add(id,sg_grp);
	else if(sub_cmd == NC_DELETE) 
	    member_delete(id,sg_grp);
	break;
    case C_HELLO:
    case C_REGISTER:
    case C_REGISTER_STOP:
    case C_JOIN_PRUNE:
    case C_ASSERT:
    case C_BOOTSTRAP:
    case C_GRAFT:
    case C_GACK:
    case C_CRP_ADV:
	cnt=0;
	sub_cmd = token_type(pkt_cmd_token,gettbl.paramtbl[cnt++]);
	if(sub_cmd == PC_SEND)
	    do_packet_send_command(cmd);
	else if(sub_cmd == PC_RECV)
	    do_packet_recv_command(cmd); 
	break;
    case C_WAIT : 
	interval = atol(gettbl.paramtbl[0]);
	wait_command(interval);
	break;
    case C_CONF_FILE:
	configp = init_config(gettbl.paramtbl[0]);
	break;
    case C_TRACE_FILE:
	id = atol(gettbl.paramtbl[0]);
	router_trace(id,gettbl.paramtbl[1]);
	break;
    case C_DUMP:
	if(gettbl.paramcount > 1) {
	    id = atol(gettbl.paramtbl[0]);	
	    if(id) {
		router_dump(id,gettbl.paramtbl[1]);
	    }
	}
	else {
	    dump();
	}
	break;
    case C_MFC: 
#if 1
	cnt=0;
	sub_cmd = token_type(net_cmd_token,gettbl.paramtbl[cnt++]);
	sg_src = char_to_inetaddr(gettbl.paramtbl[cnt++]);
	sg_grp = char_to_inetaddr(gettbl.paramtbl[cnt++]);
	if(sub_cmd == NC_ADD) {
	    net = atoi(gettbl.paramtbl[cnt++]);
	    mask = atoh(gettbl.paramtbl[cnt++]);
	    mfc_add_entry(htonl(sg_src),htonl(sg_grp),net,mask);
/*        kernel_add_entry(htonl(sg_src),htonl(sg_grp),htonl(net)): */
	}
	else if(sub_cmd == NC_DELETE) {
	    mfc_del_entry(htonl(sg_src),htonl(sg_grp));
	}
#endif
	break;
    case C_DEBUG:
	cnt=0;
	trace_mode = TRACE_MODE_PKT;
	for(;cnt<gettbl.paramcount;) {
	    sub_cmd = token_type(dbg_cmd_token,gettbl.paramtbl[cnt++]);
	    if(sub_cmd == DBG_DETAIL) {
		trace_mode = TRACE_MODE_DETAIL;
	    }
	    else if(sub_cmd == DBG_TTY) {
		open_tty(gettbl.paramtbl[cnt++]);
		trace2(NULL,"\n");
	    }
	}
	break;
    case C_NODEBUG:
	trace_mode = TRACE_MODE_NONE;
	close_tty();
	break;	
    case C_QUIT :
	rt = CNF_QUIT;
	break;
    case C_HELP:
	print_usage();
	break;
    default : 
	if(strlen(gettbl.command))
	    printf("Unknown command '%s' \n",gettbl.command);
	break;
    }

    return rt;
}

void wait_command(interval)
int interval;
{
/*    log(LOG_INFO,0,"wait %d \n",interval);*/

    wait_flag = 1;
    timer_set(wait_timer,interval);
}

void wait_timeout(id,data)
u_long id;
char *data;
{
    timer_stop(wait_timer);
    wait_flag = 0;
}

int atoh(num)
char *num;
{
    char *buf, c;
    int hex;

    buf = num;
    hex = 0;
    while(*buf) {
	hex <<= 4;
        if(*buf >= '0' && *buf <= '9') hex |= *buf - '0';
	else if (*buf >= 'a' && *buf <= 'f') hex |= 10 + (*buf-'a');
	buf++;
    }

    return hex;
}

void parse_file(fp,id)
FILE *fp;
u_long id;
{
    char c,buf[256],*bufp;
    u_long net,mask;
    int metric;

    bufp = buf;
    while((c=getc(fp)) != EOF) {
        if(c == 0x0a) {
	    *bufp = '\0';
	    metric = atoi(buf);
	    create_network_list(id,net,mask,metric);
	    bzero(buf,256);
	    bufp = buf;
	}
	else if(c == ' ' || c == '\t') {
	    *bufp = '\0';
	    prefix_parse(buf,&net,&mask);
	    bzero(buf,256);
	    bufp = buf;
	}
	else {
	    *bufp++ = c;
	}
    }
	
}

void print_usage()
{
    printf("\n");
    printf(" router-list <list> addr <router-addr> \n");
    printf(" router-list <list> hello-option <type> <len> <val> \n");
    printf(" router up | down <router-list> \n");

    printf(" \n");
    printf(" jp-list <list> upnbr <nbr_addr> holdtime <holdtime> \n");
    printf(" jp-list <list> group <group>/<masklen> join <src>/<masklen> [S] [RP] [WC] \n");
    printf(" jp-list <list> group <group>/<masklen> prune <src>/<masklen> [S] [RP] [WC] \n");

    printf(" \n");
    printf(" hello send <interface> [src <addr>] dst <addr> [hello-option <type> <len> <val>\n");
    printf(" hello recv <interface> timeout <sec> \n");
    printf(" register send <interface> [src <addr>] dst <addr> [ip <src> <dst>] [N|B]\n");
    printf(" register recv <interface> timeout <sec> \n");
    printf(" register-stop send <interface> [src <addr>] dst <addr> entry <src> <group>/<masklen> \n");
    printf(" register-stop recv <interface> timeout <sec> \n");
    printf(" join-prune send <interface> [src <addr>] dst <addr> jp-list <jp-list> \n");
    printf(" join-prune recv <interface> timeout <sec> \n");
    printf(" bootstrap send <interface> [src <addr>] [dst <addr>] bsr-list <bsr-list>\n");
    printf(" bootstrap recv <interface> timeout <sec> \n");
    printf(" assert send <interface> [src <addr>] [dst <addr>] [RP] pref <preference> metric <metric> entry <src/masklen> <group/masklen> \n");
    printf(" assert recv <interface> timeout <time> \n");
    printf(" graft/gack send <interface> [src <addr>] [dst <addr>] jp-list <jp-list> \n");
    printf(" graft/gack recv <interface> timeout <time> \n");
    printf(" crpadv send <interface> [src <addr>] [dst <addr>] crp-list <crp-list> \n");
    printf(" crpadv recv <interface> timeout <time> \n");
    printf(" \n");
    
    printf(" debug [detail] [tty <tty>] \n");
    printf(" wait <sec> \n");
    printf(" quit \n");
    printf("\n");
}

void encap_ip_packet(buf,src,dst)
char *buf;
u_long src;
u_long dst;
{
    struct ip *ipbuf;
    struct icmp *icmpp;

    ipbuf = (struct ip *)buf;
    ipbuf->ip_v = IPVERSION;
    ipbuf->ip_hl = sizeof(struct ip) >> 2;
    ipbuf->ip_tos = 0;
    ipbuf->ip_off = 0;
    ipbuf->ip_p = IPPROTO_UDP;
    ipbuf->ip_ttl = 32;
    ipbuf->ip_src.s_addr = htonl(src);
    ipbuf->ip_dst.s_addr = htonl(dst);
    ipbuf->ip_len = htons(sizeof(struct ip) + IP_DATA_SIZE);
    ipbuf->ip_sum = 0;
    ipbuf->ip_sum = in_cksum(ipbuf,20);
#if 0
    icmpp = (struct icmp *)(ipbuf+1);
    icmpp->icmp_type = ICMP_ECHO;
    icmpp->icmp_code = 0;
    icmpp->icmp_cksum = 0;
    icmpp->icmp_seq = 0;
    icmpp->icmp_cksum = in_cksum((u_short *)icmpp,sizeof(struct icmp));
#endif
}

