/* NET/ROM user command processing */

#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "ax25.h"
#include "timer.h"
#include "netrom.h"
#include "iface.h"
#include "lapb.h"
#include "cmdparse.h"
#include <ctype.h>

static int dointerface(),dobcnodes(),donodetimer(),donrnodes(),donrroute(),
	donrrts(),doobsotimer(),doparam(),donodefilter(),donrstat(),dotcpip(),
	donrexclude(),donrinfo(),donrusers();

static struct cmds nrcmds[] = {
	"bcnodes",	dobcnodes,	2,	"<interface>",	NULLCHAR,
	"exclude",	donrexclude,	0,	NULLCHAR,	NULLCHAR,
	"info",		donrinfo,	0,	NULLCHAR,	NULLCHAR,
	"interface",	dointerface,	4,
				"<interface> <alias> <quality> [lap|uplink]",NULLCHAR,
	"nodefilter",	donodefilter,	0,	NULLCHAR,	NULLCHAR,
	"nodes",	donrnodes,	0,	NULLCHAR,	NULLCHAR,
	"nodetimer",	donodetimer,	0,	NULLCHAR,	NULLCHAR,
	"obsotimer",	doobsotimer,	0,	NULLCHAR,	NULLCHAR,
	"param",	doparam,	2,	"3|4 <params>", NULLCHAR,
	"route",	donrroute,	0,	NULLCHAR,	NULLCHAR,
	"routes",	donrrts,	0,	NULLCHAR,	NULLCHAR,
#ifdef NETROM4
	"status",	donrstat,	0,	NULLCHAR,	NULLCHAR,
#endif
	"tcpip",	dotcpip,	0,	NULLCHAR,
					"normal|link|broadcast|ignore [alias]",
#ifdef NETROM4
	"users",	donrusers,	0,	NULLCHAR,	NULLCHAR,
#endif
	NULLCHAR,	NULLFP,		0,
		"?subcommands",
	NULLCHAR
};

char nr_savename[80] = "";		/* filename for route save */
char nr_infname[80] = "";		/* filename for INFO output */
#ifdef NETROM4
struct interface *nr_lap = NULLIF;	/* Local Access Point interface */
#endif

struct timer nodetimer;			/* timer for nodes broadcasts */
static struct timer obsotimer;		/* timer for aging routes */

static char nonetrom[] = "Interface \"%s\" not for NET/ROM\n";
static char baddest[] = "Bad destination callsign\n";
static char badneigh[] = "Bad neighbor callsign\n";
static char cr[] = "\r";

/* Command multiplexer */
donetrom(argc,argv)
int argc;
char *argv[];
{
	return subcmd(nrcmds,argc,argv);
}

static int dorouteadd(),doroutedrop(),dorouteinfo(),doroutesave();

static struct cmds routecmds[] = {
	"add",	dorouteadd,	7,
		"<dest> <alias> <quality> <obso> <interface> <neighbor>",
		"add failed",
	"drop", doroutedrop,	4,
		"<destination>|* <interface> <neighbor>",
		"drop failed",
	"info", dorouteinfo,	2,
		"<destination>",NULLCHAR,
	"save", doroutesave,	0,
		"[<filename>]",NULLCHAR,
	NULLCHAR,	NULLFP, 0,
		"?subcommands",
		NULLCHAR
};

/* Route command multiplexer */
static
donrroute(argc,argv)
int argc;
char *argv[];
{
	if (argc < 2) {
		doroutedump(NULLBUFP,0,1);
		return 0;
	}
	return subcmd(routecmds,argc,argv);
}

/* format alias:call in a static buffer */
char *
nr_fmtcall (rp)
struct nrroute_tab *rp;
{
	static char buf[20];
	char *cp;

	strcpy(buf,rp->alias);
	/* remove trailing spaces */
	if ((cp = index(buf,' ')) == NULLCHAR)
	    cp = &buf[strlen(buf)];
	if (cp != buf)		/* don't include colon for null alias */ 
	    *cp++ = ':';
	pax25(cp,&rp->call);
	return buf;
}

/* find out if an active crosslink exists to some neigbor */
/* return it's axp, or NULLAX25 */
static struct ax25_cb *
nrcrosslink (np)
struct nrnbr_tab *np;
{
	struct ax25_cb *find_ax25();
	struct ax25_addr neighbor,*nr_scall();

	getaxaddr(&neighbor,np->call);

	return find_ax25(&neighbor,nr_scall(np->interface));
}

/* Dump a list of known routes */
/* also called from the NODES command of NET/ROM level 7 */
doroutedump(bpp,hide,ext)
	struct mbuf **bpp;		/* append to mbuf if non-NULL */
	int hide;			/* hide #ALIAS nodes */
	int ext;			/* extended listing (includes route) */
{
	register struct nrroute_tab *rp,*srp;
	struct nrroute_tab *sortp = NULLNRRTAB;
	struct nrroute_tab **prev;
	int i,j;
	char line[80],buf[20],tmp[10];

	if(ext){			/* put header above extended listing */
	    strcpy(line,"Call      Alias  Routing       Interf  Call      Alias  Routing       Interf");
	    if(bpp != NULLBUFP){
		strcat(line,cr);
		append(bpp,qstring(line));
	    } else
		printf("%s\n",line);
	}

	line[0] = '\0';

	/* first pass: every route is put in a sorted list */
	for (i = 0; i < NRNUMCHAINS; i++)
	    for (rp = nrroute_tab[i]; rp != NULLNRRTAB; rp = rp->next) {
		if(hide && rp->alias[0] == '#')
		    continue;		/* ignore #ALIAS nodes */

		/* insert this one in the list at the proper position */
		for (srp = sortp,prev = &sortp; srp != NULLNRRTAB;
		     prev = &srp->sort, srp = srp->sort){
		    /* compare srp->call and rp->call */
		    for (j = 0; j < ALEN; j++)
			if (srp->call.call[j] != rp->call.call[j])
			    break;
		    if (j < ALEN){
			if (uchar(srp->call.call[j]) > uchar(rp->call.call[j]))
			    break;
		    } else {
			if ((srp->call.ssid & SSID) > (rp->call.ssid & SSID))
			    break;
		    }
		}
		rp->sort = srp;
		*prev = rp;
	    }

	/* second pass: the routes are printed in sorted list order */
	for (rp = sortp; rp != NULLNRRTAB; rp = rp->sort) {
	    if(!ext){
		sprintf(line + strlen(line),"%-16s ",nr_fmtcall(rp));
	    } else {
		pax25(tmp,&rp->call);
		sprintf(line + strlen(line),"%-10s%-7s",tmp,rp->alias);
		if (rp->routes == NULLNRBIND){
		    sprintf(line + strlen(line),"%-22s","no route");
		} else {
		    if(addreq(&rp->call,rp->routes->via->call)){
			strcpy(buf,"direct");
		    } else {
			pax25(tmp,rp->routes->via->call);
			sprintf(buf,"via %-9s",tmp);
		    }
		    sprintf(line + strlen(line),"%-13s %-8s",
			    buf,rp->routes->via->interface->name);
		}
	    }
	    if (strlen(line) > 65) {
		if(bpp != NULLBUFP){
		    strcat(line,cr);
		    append(bpp,qstring(line));
		} else
		    printf("%s\n",line);
		line[0] = '\0';
	    }
	}

	if (line[0])
	    if(bpp != NULLBUFP){
		strcat(line,cr);
		append(bpp,qstring(line));
	    } else
		printf("%s\n",line);

	return 0;
}

/* print detailed information on an individual route */
static
dorouteinfo(argc,argv)
int argc;
char *argv[];
{
	register struct nrroute_tab *rp;
	struct ax25_addr dest;

	if (setcall(&dest,argv[1]) < 0) {
	    printf(baddest);
	    return -1;
	}

	if ((rp = find_nrroute(&dest)) == NULLNRRTAB) {
	    printf("no such route\n");
	    return -1;
	}

	printf("CP Qual Obso Interf Neighbor     Node: ");
	nrrouteinfo(NULLBUFP,rp);
	return 0;
}

/* show route info for some callsign */
/* also called from the NODES command of NET/ROM level 7 */
nrrouteinfo (bpp,rp)
struct mbuf **bpp;			/* append to this mbuf if non-null */
register struct nrroute_tab *rp;

{
	register struct nr_bind *bp;
	register struct nrnbr_tab *np;
	char neighbor[60];
	char line[80];

	strcpy(line,nr_fmtcall(rp));
	if(bpp != NULLBUFP){
	    strcat(line,cr);
	    append(bpp,qstring(line));
	} else
	    printf("%s\n",line);

	for (bp = rp->routes; bp != NULLNRBIND; bp = bp->next) {
	    np = bp->via;
	    psax25(neighbor,np->call);
	    sprintf(line,"%c%c  %3d  %3d %-6s %s",
			(nrcrosslink(np) != NULLAX25 ? '>' : ' '),
			(bp->flags & NRB_PERMANENT ? 'P' : ' '),
			bp->quality,bp->obsocnt,
			np->interface->name,
			neighbor);

	    if(bpp != NULLBUFP){
		strcat(line,cr);
		append(bpp,qstring(line));
	    } else
		printf("%s\n",line);
	}
}

/* show routes to NET/ROM neighbors */
/* also called from the ROUTES command of NET/ROM level 7 */
nrrtsdump (bpp)
struct mbuf **bpp;			/* append to this mbuf if non-null */
{
	struct nrnbr_tab *np;
	int i;
	char tmp[60];
	char line[80];

	for (i = 0; i < NRNUMCHAINS; i++)
	    for (np = nrnbr_tab[i]; np != NULLNTAB; np = np->next){
		psax25(tmp,np->call);
		sprintf(line,"%c %-6s %3d %3d %-9s",
			(nrcrosslink(np) != NULLAX25 ? '>' : ' '),
			np->interface->name,
			np->interface->nriface->quality,
			np->refcnt,tmp);

		if (np->failcnt != 0) {
		    sprintf(tmp,"  (%d link failure%s)",
			np->failcnt,(np->failcnt != 1)? "s":"");
		    strcat(line,tmp);
		}

		if(bpp != NULLBUFP){
		    strcat(line,cr);
		    append(bpp,qstring(line));
		} else
		    printf("%s\n",line);
	    }
}

/* set filename for INFO output */
static
donrinfo(argc,argv)
int argc;
char *argv[];
{
	if (argc < 2)
	    printf("%s\n",nr_infname);
	else
	    strcpy(nr_infname,argv[1]);

	return 0;
}

/* save routing info, or set filename for saves */
static
doroutesave(argc,argv)
int argc;
char *argv[];
{
	if (argc < 2)
	    nr_routesave();
	else
	    strcpy(nr_savename,argv[1]);

	return 0;
}

/* display nodes like in NET/ROM "nodes" command */
static
donrnodes(argc,argv)
int argc;
char *argv[];
{
	int hide = 0;

	if (argc < 2 || argv[1][0] != '*')
		hide = 1;

	doroutedump(NULLBUFP,hide,0);
	return 0;
}

/* display routes like in NET/ROM "routes" command */
static
donrrts(argc,argv)
int argc;
char *argv[];
{
	nrrtsdump(NULLBUFP);
	return 0;
}

/* convert a null-terminated alias name to a blank-filled */
/* version. It is no longer upcased. Return -1 on failure. */
static int
putalias(to,from)
register char *to,*from;
{
	int len,i;

	if ((len = strlen(from)) > ALEN) {
	    printf("alias too long - six characters max\n");
	    return -1;
	}

	for (i = 0; i < ALEN; i++) {
	    if (i < len) {
		if (*from < ' ' || *from >= 127) {
		    printf("illegal char in alias\n");
		    return -1;
		}

		*to++ = *from++;
	    } else
		*to++ = ' ';
	}

	*to = '\0';
	return 0;
}

/* Add a route */
/* Usage: netrom route add <dest> <alias> <quality> <obso> <interface> <neighbor> */
static
dorouteadd(argc,argv)
int argc;
char *argv[];
{
	char alias[7];
	struct ax25_addr dest;
	unsigned quality,obsocnt,perm;
	char neighbor[AXALEN * 3];
	register struct interface *ifp;
	int naddr;

	/* format destination callsign */
	if (setcall(&dest,argv[1]) < 0) {
		printf(baddest);
		return -1;
	}

	/* format alias (putalias prints error message if necessary) */
	if (putalias(alias,argv[2]) < 0)
		return -1;

	/* get and check quality value */
	if ((quality = atoi(argv[3])) > 255) {
		printf("maximum route quality is 255\n");
		return -1;
	}

	/* get and check obsoletion count value. 0 means permanent. */
	if ((obsocnt = atoi(argv[4])) > 255) {
		printf("maximum obsoletion count is 255\n");
		return -1;
	}

	if (obsocnt == 0){
		perm = 1;
		obsocnt = obso_init;
	} else {
		perm = 0;
	}

	/* find interface */
	if ((ifp = ifunit(argv[5])) == NULLIF)
		return -1;
	if (ifp->nriface == NULLNRIFACE){
		printf(nonetrom,argv[5]);
		return -1;
	}

	/* make sure no more than 2 digis */
	naddr = argc - 6;
	if (naddr > 3) {
		printf("no more than 2 digipeaters for a NET/ROM neighbor\n");
		return -1;
	}

	/* format neighbor address string */
	setpath(neighbor,&argv[6],naddr);

	if (nr_rt_add(alias,&dest,ifp,quality,obsocnt,neighbor,perm) < 0)
		return -1;

	return 0;
}


/* drop a route */
/* Usage: netrom route drop <destination> <interface> <neighbor> */
static
doroutedrop(argc,argv)
int argc;
char *argv[];
{
	struct ax25_addr *dp,dest,neighbor;
	register struct interface *ifp;

	/* check for * = all destinations via this neighbor */
	if (!strcmp(argv[1],"*"))
	    dp = NULLAXADDR;			/* NULL to nr_rt_drop */
	else
	    /* format destination callsign */
	    if (setcall(dp = &dest,argv[1]) < 0) {
		printf(baddest);
		return -1;
	    }
	/* find interface */
	if ((ifp = ifunit(argv[2])) == NULLIF)
		return -1;
	if (ifp->nriface == NULLNRIFACE){
		printf(nonetrom,argv[2]);
		return -1;
	}
	if (setcall(&neighbor,argv[3]) < 0) {
		printf(badneigh);
		return -1;
	}

	return nr_rt_drop(dp,&neighbor,ifp);
}

/* route save routine. creates file with netrom route add commands... */
static
nr_routesave()
{
	FILE *savefile;
	register struct nrroute_tab *rp;
	register struct nr_bind *bp;
	int i;
	char *p;
	char tmp1[10],tmp2[10],tmp3[60];

	if (nr_savename[0] == '\0')
		return;

	if ((savefile = fopen(nr_savename,"w")) == NULLFILE)
		return;

	fprintf(savefile,"# NET/ROM routes (auto-saved):\n");
	for (i = 0; i < NRNUMCHAINS; i++) {
	    for (rp = nrroute_tab[i]; rp != NULLNRRTAB; rp = rp->next) {
		pax25(tmp1,&rp->call);
		strcpy(tmp2,rp->alias);
		for (p = tmp2; *p != '\0'; p++)
		    if (*p == ' ')
			*p = '\0';

		for (bp = rp->routes; bp != NULLNRBIND; bp = bp->next) {
		    psax25(tmp3,bp->via->call);
		    fprintf(savefile,"netrom route add %s '%s' %d %d %s %s\n",
			    tmp1,tmp2,bp->quality,bp->obsocnt,
			    bp->via->interface->name,tmp3);
		}
	    }
	}

	fclose(savefile);
}

/* display or set netrom level 3 or 4 parameters */
static int
doparam(argc,argv)
int argc;
char *argv[];
{
	switch(argv[1][0])
	{
	case '3':
	case 'n':
	    if(argc == 2){
		printf("Autofloor=%u Obso_init=%u Obso_minbc=%u TTL=%u Maxroutes=%u Maxqueue=%u Maxfail=%u\n",
		       nr_autofloor,obso_init,obso_minbc,nr_ttl,nr_maxroutes,nr_maxqueue,nr_maxfail);
		return 0;
	    }
	    if(argc > 2)
		nr_autofloor = atoi(argv[2]);
	    if(argc > 3)
		obso_init = atoi(argv[3]);
	    if(argc > 4)
		obso_minbc = atoi(argv[4]);
	    if(argc > 5)
		nr_ttl = atoi(argv[5]);
	    if(argc > 6)
		nr_maxroutes = atoi(argv[6]);
	    if(argc > 7)
		nr_maxqueue = atoi(argv[7]);
	    if(argc > 8)
		nr_maxfail = atoi(argv[8]);
	    break;

	case '4':
	case 't':
#ifdef NETROM4
	    if(argc == 2){
		printf("Timeout=%u Tries=%u Ackdelay=%u Busydelay=%u Window=%u Backlog=%u Inactive=%u\n",
		       nr_trtimeout,nr_trtries,nr_trackdelay,nr_trbusdelay,
		       nr_trwindow,nr_trbacklog,nr_noactive);
		return 0;
	    }
	    if(argc > 2)
		nr_trtimeout = atoi(argv[2]);
	    if(argc > 3)
		nr_trtries = atoi(argv[3]);
	    if(argc > 4)
		nr_trackdelay = atoi(argv[4]);
	    if(argc > 5)
		nr_trbusdelay = atoi(argv[5]);
	    if(argc > 6)
		nr_trwindow = atoi(argv[6]);
	    if(argc > 7)
		nr_trbacklog = atoi(argv[7]);
	    if(argc > 8)
		nr_noactive = atoi(argv[8]);
#endif
	    break;

	default:
	    printf("level must be 3 (network) or 4 (transport)\n");
	    return -1;
	}
	return 0;
}

/* Display or change netrom excluded node callsigns
 * usage:
 * netrom exclude <callsign> [d]
 * netrom exclude
 */
static
donrexclude(argc,argv)
int argc;
char *argv[];
{
	register struct ax25_call *axc;
	struct ax25_addr call;
	int i,n;
	char tmp[10];

	if(argc < 2){
	    for (i = n = 0; i < NHASH; i++){
		for (axc = nr_excl[i]; axc != NULLAXCALL; axc = axc->next){
		    pax25(tmp,&axc->addr);
		    printf("%-6s%s",tmp,(++n % 8) == 0? "\n":"  ");
		}
	    }
	    if ((n % 8) != 0)
		printf("\n");
	    return 0;
	}

	if(setcall(&call,argv[1]) < 0){
		printf("Usage: netrom exclude <callsign> [d]\n");
		return -1;
	}

	call.ssid = 0;			/* always use the -0 SSID */

	if(argc > 2 && !strcmp(argv[2],"d")){
		if((axc = find_axcall(nr_excl,&call)) != NULLAXCALL){
			del_axcall(nr_excl,axc);
			return 0;
		} else {
			pax25(tmp,&call);
			printf("%s not in exclude list\n",tmp);
			return -1;
		}
	}

	if((axc = cr_axcall(nr_excl,&call)) == NULLAXCALL)
		return -1;

	axc->flags = 0;
	return 0;
}

#ifdef NETROM4
/* show status of NET/ROM transport-layer connections */
static int
donrstat(argc,argv)
int argc;
char *argv[];
{
	int idx;
	register struct nr_circ *circ;
	char user[10],node[10];

	printf("Local  Remote User      @Node     S Wi TxS TxA RxS C S R Tx Rx TrnT AckT NoaT\n");

	for (idx = 0; idx < NR4NUMCIRC; idx++)
	    if ((circ = nr_circ[idx]) != NULLNRCIRC) {
		pax25(user,&circ->suser);
		pax25(node,&circ->dnode);
		printf("%2d/%-3d %2d/%-3d %-9s %-9s %c %2d %3d %3d %3d %d %d %d %2d %2d",
			circ->my.index,uchar(circ->my.id),
			circ->your.index,uchar(circ->your.id),
			user,node,
			((circ->state < 4)? "RPCD"[circ->state] : '?'),
			circ->window,circ->txseq,circ->txack,circ->rxseq,
			circ->r_choked,circ->s_choked,circ->retries,
			len_q(circ->txq),len_q(circ->rxq));
		if (run_timer(&circ->trtimer))
			printf("%5ld",TICK2SEC(circ->trtimer.start - circ->trtimer.count));
		else
			printf(" stop");
		if (run_timer(&circ->acktimer))
			printf("%5ld",TICK2SEC(circ->acktimer.start - circ->acktimer.count));
		else
			printf(" stop");
		if (run_timer(&circ->noacttim))
			printf("%5ld\n",TICK2SEC(circ->noacttim.start - circ->noacttim.count));
		else
			printf(" stop\n");
	    }

	return 0;
}

/* show users of NET/ROM (like "users" command on the NR7 interpreter) */
static int
donrusers(argc,argv)
int argc;
char *argv[];
{
	nruserdump(NULLBUFP);		/* show users to screen */
	return 0;
}
#endif

/* set and display the TCP/IP netrom node special handling */
static int
dotcpip(argc,argv)
int argc;
char *argv[];
{
	if (argc == 1){
	    printf("Non-%s node handling: ",nr_aliasip);
	    switch (nr_tcpip)
	    {
	    case NRT_NORM:
		printf("normal\n");
		break;

	    case NRT_ILNK:
		printf("broadcast on interlink\n");
		break;

	    case NRT_NOBR:
		printf("no broadcast\n");
		break;

	    case NRT_IGN:
		printf("ignore in broadcast\n");
		break;
	    }
	} else {
	    switch (tolower(argv[1][0]))
	    {
	    case 'n':
		nr_tcpip = NRT_NORM;
		break;

	    case 'l':
		nr_tcpip = NRT_ILNK;
		break;

	    case 'b':
		nr_tcpip = NRT_NOBR;
		break;

	    case 'i':
		nr_tcpip = NRT_IGN;
		break;

	    default:
		return -1;
	    }

	    if (argc > 2)
		if (putalias(nr_aliasip,argv[2]) != 0)
		    return 1;
	}

	return 0;
}

/* make an interface available to NET/ROM */
static int
dointerface(argc,argv)
int argc;
char *argv[];
{
	register struct interface *ifp;

	if (nr_interface == NULLIF) {
		printf("Attach netrom interface first\n");
		return 1;
	}

	if((ifp = ifunit(argv[1])) == NULLIF)
		return 1;

	if (!(ifp->flags & IF_AX25)) {
		printf("Must be an AX.25 interface\n");
		return 1;
	}

	if (ifp->nriface != NULLNRIFACE){
		printf("Interface \"%s\" is already registered\n",argv[1]);
		return 1;
	}

	if ((ifp->nriface = (struct nriface *) calloc(1,sizeof(struct nriface)))
		== NULLNRIFACE)
		return -1;

	if (putalias(ifp->nriface->alias,argv[2]) < 0)
		return 1;

	ifp->nriface->quality = uchar(atoi(argv[3]));

#ifdef NETROM4
	if (argc > 4)			/* a fourth arg given? */
	    switch (argv[4][0]) {
	    case 'l':			/* lap */
	    case 'L':
		nr_lap = ifp;		/* this iface is the LAP */
					/* this implies it has uplink */
	    case 'u':			/* uplink */
	    case 'U':
		ifp->nriface->uplink = 1;
		break;
	    }
#endif

	return 0;
}

/* Broadcast nodes list on named interface. */

static int
dobcnodes(argc,argv)
int argc;
char *argv[];
{
	register struct interface *ifp;

	if ((ifp = ifunit(argv[1])) == NULLIF)
		return -1;
	if (ifp->nriface == NULLNRIFACE){
		printf(nonetrom,argv[1]);
		return -1;
	}

	nr_bcnodes(ifp);
	return 0;
}

/* Set outbound node broadcast interval */
static int
donodetimer(argc,argv)
int argc;
char *argv[];
{
	int donodetick();

	if(argc < 2){
	    printf("%lu/%lu\n", TICK2SEC(nodetimer.start - nodetimer.count),
				TICK2SEC(nodetimer.start));
	    return 0;
	}
	stop_timer(&nodetimer);			/* in case it's already running */ 
	nodetimer.func = (void (*)())donodetick;/* what to call on timeout */
	nodetimer.arg = NULLCHAR;		/* dummy value */
	nodetimer.start = SEC2TICK(atol(argv[1]));	/* set timer duration */
	nodetimer.count = 0;			/* in case 0 time specified */

	start_timer(&nodetimer);		/* and fire it up */
	return 0;
}

static int
donodetick()
{
	register struct interface *ifp;

	for (ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
		if (ifp->nriface != NULLNRIFACE)
			nr_bcnodes(ifp);

	/* Restart timer */
	start_timer(&nodetimer);
}

/* Set timer for aging routes */
static int
doobsotimer(argc,argv)
int argc;
char *argv[];
{
	extern int doobsotick();

	if(argc < 2){
	    printf("%lu/%lu\n", TICK2SEC(obsotimer.start - obsotimer.count),
				TICK2SEC(obsotimer.start));
	    return 0;
	}
	stop_timer(&obsotimer);			/* just in case it's already running */
	obsotimer.func = (void (*)())doobsotick;/* what to call on timeout */
	obsotimer.arg = NULLCHAR;		/* dummy value */
	if ((obsotimer.start = SEC2TICK(atol(argv[1]))) == 0)/* set timer duration */
	    obsotimer.start = SEC2TICK(3600L);

	start_timer(&obsotimer);		/* and fire it up */
	return 0;
}


/* Go through the routing table,reducing the obsolescence count of
 * non-permanent routes,and purging them if the count reaches 0
 */
static int
doobsotick()
{
	register struct nrroute_tab *rp;
	struct nrroute_tab *rpnext;
	register struct nr_bind *bp;
	struct nr_bind *bpnext;
	int i;

	for (i = 0; i < NRNUMCHAINS; i++) {
	    for (rp = nrroute_tab[i]; rp != NULLNRRTAB; rp = rpnext) {
		rpnext = rp->next;		/* save in case we free this route */
		for (bp = rp->routes; bp != NULLNRBIND; bp = bpnext) {
		    bpnext = bp->next;		/* in case we free this binding */
		    if (bp->flags & NRB_PERMANENT)  /* don't age these */
			continue;
		    if (--bp->obsocnt == 0)	/* time's up! */
			nr_rnb_drop(rp,bp->via,bp); /* drop it */
		}
	    }
	}

	nr_routesave();			/* save changed state */
	start_timer(&obsotimer);
}


static int donfadd(),donfdrop(),donfmode();

static struct cmds nfcmds[] = {
	"add",	donfadd,	3,
		"<neighbor> <interface>",
		"add failed",
	"drop",donfdrop,	3,
		"<neighbor> <interface>",
		"drop failed",
	"mode",donfmode,	0,	NULLCHAR,	NULLCHAR,
	NULLCHAR,	NULLFP,0,
		"?subcommands",
		NULLCHAR
};

/* nodefilter command multiplexer */
static
donodefilter(argc,argv)
int argc;
char *argv[];
{
	if (argc < 2) {
		donfdump();
		return 0;
	}
	return subcmd(nfcmds,argc,argv);
}

/* display a list of <callsign,interface> pairs from the filter
 * list.
 */
static
donfdump()
{
	int i,column = 1;
	struct nrnf_tab *fp;
	char buf[16];

	printf("Neighbor  Interface Neighbor  Interface Neighbor  Interface Neighbor  Interface\n");
	for (i = 0; i < NRNUMCHAINS; i++)
	    for (fp = nrnf_tab[i]; fp != NULLNRNFTAB; fp = fp->next) {
		pax25(buf,&fp->neighbor);
		printf("%-9s %-8s%s",
		       buf,fp->interface->name,
		       (column++ == 4)? (column = 1,"\n"):"  ");
	    }

	if (column != 1)
	    printf("\n");

	return 0;
}

/* add an entry to the filter table */
static
donfadd(argc,argv)
int argc;
char *argv[];
{
	struct ax25_addr neighbor;
	struct interface *ifp;

	/* format callsign */
	if (setcall(&neighbor,argv[1]) < 0) {
		printf(badneigh);
		return -1;
	}

	/* find interface */
	if ((ifp = ifunit(argv[2])) == NULLIF)
		return -1;
	if (ifp->nriface == NULLNRIFACE){
		printf(nonetrom,argv[2]);
		return -1;
	}

	return nr_nfadd(&neighbor,ifp);
}

/* drop an entry from the filter table */
static
donfdrop(argc,argv)
int argc;
char *argv[];
{
	struct ax25_addr neighbor;
	struct interface *ifp;

	/* format neighbor callsign */
	if (setcall(&neighbor,argv[1]) < 0) {
		printf(badneigh);
		return -1;
	}

	/* find interface */
	if ((ifp = ifunit(argv[2])) == NULLIF)
		return -1;
	if (ifp->nriface == NULLNRIFACE){
		printf(nonetrom,argv[2]);
		return -1;
	}

	return nr_nfdrop(&neighbor,ifp);
}

/* nodefilter mode subcommand */
static
donfmode(argc,argv)
int argc;
char *argv[];
{
	if (argc < 2) {
		printf("filter mode is ");
		switch (nr_nfmode) {
			case NRNF_NOFILTER:
				printf("none\n");
				break;
			case NRNF_REJECT:
				printf("reject\n");
				break;
			case NRNF_ACCEPT:
				printf("accept\n");
				break;
			case NRNF_EXCLUSIVE:
				printf("exclusive\n");
				break;
		}
		return 0;
	}

	switch (argv[1][0]) {
		case 'n':
		case 'N':
			nr_nfmode = NRNF_NOFILTER;
			break;
		case 'r':
		case 'R':
			nr_nfmode = NRNF_REJECT;
			break;
		case 'a':
		case 'A':
			nr_nfmode = NRNF_ACCEPT;
			break;
		case 'e':
		case 'E':
			nr_nfmode = NRNF_EXCLUSIVE;
			break;
		default:
			printf("modes are: none reject accept exclusive\n");
			return -1;
	}

	return 0;
}
