/* AX25 control commands
 * Copyright 1991 Phil Karn, KA9Q
 * 
 * Mods by G1EMM
 * Mods by PA0GRI
 * Mods by N1BEE
 */
/*
** FILE: ax25cmd.c
**
** AX.25 command handler.
**
** 09/24/90 Bob Applegate, wa2zzx
**    Added BCTEXT, BC, and BCINTERVAL commands for broadcasting an id
**    string using UI frames.
**
** 27/09/91 Mike Bilow, N1BEE
**    Added Filter command for axheard control
*/

#include <stdio.h>
#ifdef MSDOS
#include <dos.h>
#endif
#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "timer.h"
#include "proc.h"
#include "iface.h"
#include "ax25.h"
#include "lapb.h"
#include "cmdparse.h"
#include "socket.h"
#include "mailbox.h"
#include "session.h"
#include "tty.h"
#include "nr4.h"
#include "commands.h"
#include "pktdrvr.h"
#include "netrom.h" 

int axheard __ARGS((struct iface *ifp));
static void axflush __ARGS((struct iface *ifp));
static int doaxfilter __ARGS((int argc,char *argv[],void *p));
static int doaxflush __ARGS((int argc,char *argv[],void *p));
static int doaxirtt __ARGS((int argc,char *argv[],void *p));
static int doaxkick __ARGS((int argc,char *argv[],void *p));
static int doaxreset __ARGS((int argc,char *argv[],void *p));
static int doaxroute __ARGS((int argc,char *argv[],void *p));
static int doaxstat __ARGS((int argc,char *argv[],void *p));
static int doaxwindow __ARGS((int argc,char *argv[],void *p));
static int dobc __ARGS((int argc,char *argv[],void *p));
static int dobcint __ARGS((int argc,char *argv[],void *p));
static int dobcport __ARGS((int argc,char *argv[],void *p));
static int dobctext __ARGS((int argc,char *argv[],void *p));
static int doaxhport __ARGS((int argc,char *argv[],void *p));
static int doaxhsize __ARGS((int argc,char *argv[],void *p));
static int doblimit __ARGS((int argc,char *argv[],void *p));
static int dodigipeat __ARGS((int argc,char *argv[],void *p));
static int domaxframe __ARGS((int argc,char *argv[],void *p));
static int domycall __ARGS((int argc,char *argv[],void *p));
static int don2 __ARGS((int argc,char *argv[],void *p));
static int dopaclen __ARGS((int argc,char *argv[],void *p));
static int dopthresh __ARGS((int argc,char *argv[],void *p));
static int dot3 __ARGS((int argc,char *argv[],void *p));
static int doaxtype __ARGS((int argc,char *argv[],void *p));
static int dot4 __ARGS((int argc,char *argv[],void *p));
static int doaxversion __ARGS((int argc,char *argv[],void *p));
static int dotutorcall __ARGS((int argc,char *argv[],void *p));
static int dorosecall __ARGS((int argc,char *argv[],void *p));
static int doinfocall __ARGS((int argc,char *argv[],void *p));
static int donewscall __ARGS((int argc,char *argv[],void *p));
void dobroadtick __ARGS((void));
int donralias __ARGS((int argc,char *argv[],void *p));
#ifdef AXBCSTR
static void ax_bc __ARGS((struct iface *axif, char *str));
#else
static void ax_bc __ARGS((struct iface *axif));
#endif
static int axdest __ARGS((struct iface *ifp));
#ifdef TTYCALL
static int dottycall __ARGS((int argc, char *argv[], void *p));
extern char Ttycall[AXALEN];	/* the ttylink call in 'call' form */
#endif

extern int axheard_filter_flag;		/* in axheard.c */
/* Defaults for IDing. */
char *axbctext = NULL;				/* Text to send */
static struct timer Broadtimer;	/* timer for broadcasts */
extern char Tcall[AXALEN];
extern char Icall[AXALEN];
extern char Ncall[AXALEN];
extern char Myalias[AXALEN];
extern char Nralias[ALEN+1];
char AXRosecall[AXALEN];

char *Ax25states[] = {
	"",
	"Disconnected",
	"Listening",
	"Conn pending",
	"Disc pending",
	"Connected",
	"Recovery",
};

/* Ascii explanations for the disconnect reasons listed in lapb.h under
 * "reason" in ax25_cb
 */
char *Axreasons[] = {
	"Normal",
	"DM received",
	"Timeout"
};

static struct cmds DFAR Axcmds[] = {
	"alias",	donralias,	0, 0, NULLCHAR,
	"bc",		dobc,		0, 0, NULLCHAR,
	"bcinterval",	dobcint,	0, 0, NULLCHAR,
	"bcport",	dobcport,	0, 0, NULLCHAR,
	"blimit",	doblimit,	0, 0, NULLCHAR,
	"bctext",	dobctext,	0, 0, NULLCHAR,
	"dest",		doaxdest,	0, 0, NULLCHAR,
	"digipeat",	dodigipeat,	0, 0, NULLCHAR,
	"filter",	doaxfilter,	0, 0, NULLCHAR,
	"flush",	doaxflush,	0, 0, NULLCHAR,
	"heard",	doaxheard,	0, 0, NULLCHAR,
	"hearddest",	doaxdest,	0, 0, NULLCHAR,
	"hport",	doaxhport,	0, 0, NULLCHAR,
	"hsize",	doaxhsize,	0, 0, NULLCHAR,
#ifdef TUTOR
	"infocall",     doinfocall,     0, 0, NULLCHAR,
#endif
	"irtt",		doaxirtt,	0, 0, NULLCHAR,
	"kick",		doaxkick,	0, 2, "ax25 kick <axcb>",
	"maxframe",	domaxframe,	0, 0, NULLCHAR,
	"mycall",	domycall,	0, 0, NULLCHAR,
#ifdef TUTOR
	"newscall",     donewscall,     0, 0, NULLCHAR,
#endif
	"paclen",	dopaclen,	0, 0, NULLCHAR,
	"pthresh",	dopthresh,	0, 0, NULLCHAR,
	"reset",	doaxreset,	0, 2, "ax25 reset <axcb>",
	"retry",	don2,		0, 0, NULLCHAR,
	"rosecall",	dorosecall,	0, 0, NULLCHAR,
	"route",	doaxroute,	0, 0, NULLCHAR,
	"status",	doaxstat,	0, 0, NULLCHAR,
	"t3",		dot3,		0, 0, NULLCHAR,
	"t4",		dot4,		0, 0, NULLCHAR,
	"timertype",	doaxtype,	0, 0, NULLCHAR,
#ifdef TUTOR
	"tutorcall",    dotutorcall,    0, 0, NULLCHAR,
#endif
#ifdef TTYCALL
	"ttycall",	dottycall,	0, 0, NULLCHAR,
#endif
	"version",	doaxversion,	0, 0, NULLCHAR,
	"window",	doaxwindow,	0, 0, NULLCHAR,
	NULLCHAR,
};
/* Multiplexer for top-level ax25 command */
int
doax25(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(Axcmds,argc,argv,p);
}



#ifdef MAILBOX
#include "mailbox.h"
#endif

int
donralias(argc,argv,p)
int argc ;
char *argv[] ;
void *p;
{
    char tmp[AXBUF];

    if(argc < 2) {
	tprintf("%s\n",pax25(tmp,Myalias));
	return 0;
    }
    if( (setcall(Myalias,argv[1]) == -1)
#ifdef NETROM
       || (putalias(Nralias,argv[1],1) == -1)
#endif
					     ){
	    tputs("can't set alias\n");
	    Myalias[0] = '\0';
#ifdef NETROM
	    Nralias[0] = '\0';
#endif
	    return 0;
    }
#ifdef MAILBOX
    setmbnrid();
#endif
    return 0;
}

/*
** This function is called to send the current broadcast message
** and reset the timer.
*/

static int dobc(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifa;

	if (argc < 2)	{
		tprintf("you need to specify an interface\n");
		return 1;
	}

	ifa = Ifaces;
	while (ifa != NULL && stricmp(ifa->name,argv[1]))
		ifa = ifa->next;
	if (ifa == NULL)
		tprintf("unknown interface\n");
	else if (ifa->type != CL_AX25)
		tprintf("not an AX.25 interface\n");
#ifdef AXBCSTR
	else if (argc > 2)
		ax_bc(ifa, argv[2]);
#endif
	else	{
#ifdef AXBCSTR
		ax_bc(ifa, axbctext);
#else
		ax_bc(ifa);
#endif
		stop_timer(&Broadtimer) ;	/* in case it's already running */
		start_timer(&Broadtimer);		/* and fire it up */
	}
	return 0;
}



/*
** View/Change the message we broadcast.
*/

static int dobctext(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if (argc < 2)
		tprintf("Broadcast text: %s\n",axbctext);
	else	{
		if (axbctext != NULL) free(axbctext);
		axbctext = malloc(strlen(argv[1]) + 1);
		strcpy(axbctext,argv[1]);
	}
	return 0;
}


#define TICKSPERSEC	(1000L / MSPTICK)	/* Ticks per second */

/*
** Examine/change the broadcast interval.
*/

static int dobcint(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2)	{
		tprintf("Broadcast timer %lu/%lu seconds\n",
			read_timer(&Broadtimer)/1000L,
			dur_timer(&Broadtimer)/1000L);
		return 0;
	}
	stop_timer(&Broadtimer) ;	/* in case it's already running */
	Broadtimer.func = (void (*)__ARGS((void*)))dobroadtick;/* what to call on timeout */
	Broadtimer.arg = NULLCHAR;		/* dummy value */
	set_timer(&Broadtimer,atoi(argv[1])*1000L);	/* set timer duration */
	start_timer(&Broadtimer);		/* and fire it up */
	return 0;
}

int Maxax25heard;

/* Set the size of the ax.25 heard list */
int doaxhsize(argc, argv, p)
int argc;
char *argv[];
void *p;
{
    return setint(&Maxax25heard,"Max ax-heard",argc,argv);
}

void
#ifdef PROTOTYPES
dobroadtick(void)
#else
dobroadtick()
#endif
{
	struct iface *ifa;

	ifa = Ifaces;

	while (ifa != NULL)	{
	        if (ifa->flags & AX25_BEACON)
#ifdef AXBCSTR
			ax_bc(ifa, axbctext);
#else
			ax_bc(ifa);
#endif
		ifa = ifa->next;
	}

	/* Restart timer */
	start_timer(&Broadtimer) ;
}



/*
** This is the low-level broadcast function.
*/

#ifdef AXBCSTR
static void ax_bc(axif, axbctext)
char *axbctext;
#else
static void ax_bc(axif)
#endif
struct iface *axif;
{
	struct mbuf *hbp;
	int i;
	
	/* prepare the header */
	i = strlen(axbctext);
	if((hbp = alloc_mbuf(i)) == NULLBUF)
		return;
		
	hbp->cnt = i;	
	memcpy(hbp->data,axbctext,(size_t)i);

    (*axif->output)(axif, Ax25multi[IDCALL], axif->hwaddr,
		PID_NO_L3, hbp);	/* send it */

	/*
	** Call another function to reset the timer...
	reset_bc_timer();
	*/
}


int
doaxheard(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;

	if(argc > 1){
		if((ifp = if_lookup(argv[1])) == NULLIF){
            tprintf(Badinterface,argv[1]);
			return 1;
		}
		if(ifp->output != ax_output){
			tprintf("Interface %s not AX.25\n",argv[1]);
			return 1;
		}
		axheard(ifp);
		return 0;
	}
	for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next){
		if(ifp->output != ax_output)
			continue;	/* Not an ax.25 interface */
		if(axheard(ifp) == EOF)
			break;
	}
	return 0;
}
int
axheard(ifp)
struct iface *ifp;
{
	int col = 0;
	struct lq *lp;
	char tmp[AXBUF];

	if(ifp->hwaddr == NULLCHAR)
		return 0;

	tprintf("Interface  Station   Time since send  Pkts sent\n");
	tprintf("%-9s  %-9s   %12s    %7lu\n",ifp->name,pax25(tmp,ifp->hwaddr),
	 tformat(secclock() - ifp->lastsent),ifp->rawsndcnt);

	tprintf("Station   Time since heard Pkts rcvd : ");
	tprintf("Station   Time since heard Pkts rcvd\n");
	for(lp = Lq;lp != NULLLQ;lp = lp->next){
		if(lp->iface != ifp)
			continue;
		if(col)
			tprintf("  : ");
		if(tprintf("%-9s   %12s    %7lu",pax25(tmp,lp->addr),
		 tformat(secclock() - lp->time),lp->currxcnt) == EOF)
			return EOF;
		if(col){
			if(tprintf("\n") == EOF){
				return EOF;
			} else {
				col = 0;
			}
		} else {
			col = 1;
		}
	}
	if(col)
		tprintf("\n");
	return 0;
}
int
doaxdest(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;

	if(argc > 1){
		if((ifp = if_lookup(argv[1])) == NULLIF){
            tprintf(Badinterface,argv[1]);
			return 1;
		}
		if(ifp->output != ax_output){
			tprintf("Interface %s not AX.25\n",argv[1]);
			return 1;
		}
		axdest(ifp);
		return 0;
	}
	for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next){
		if(ifp->output != ax_output)
			continue;	/* Not an ax.25 interface */
		if(axdest(ifp) == EOF)
			break;
	}
	return 0;
}
static int
axdest(ifp)
struct iface *ifp;
{
	struct ld *lp;
	struct lq *lq;
	char tmp[AXBUF];

	if(ifp->hwaddr == NULLCHAR)
		return 0;
	tprintf("%s:\n",ifp->name);
	tprintf("Station   Last ref         Last heard           Pkts\n");
	for(lp = Ld;lp != NULLLD;lp = lp->next){
		if(lp->iface != ifp)
			continue;

		tprintf("%-10s%-17s",
		 pax25(tmp,lp->addr),tformat(secclock() - lp->time));

		if(addreq(lp->addr,ifp->hwaddr)){
			/* Special case; it's our address */
			tprintf("%-17s",tformat(secclock() - ifp->lastsent));
		} else if((lq = al_lookup(ifp,lp->addr,0)) == NULLLQ){
			tprintf("%-17s","");
		} else {
			tprintf("%-17s",tformat(secclock() - lq->time));
		}
		if(tprintf("%8lu\n",lp->currxcnt) == EOF)
			return EOF;
	}
	return 0;
}

static int
doaxfilter(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc >= 2){
		setint(&axheard_filter_flag,"ax25 heard filter",argc,argv);
	} else {
		tprintf("Usage: ax25 filter <0|1|2|3>\n");
		return 1;
	}

	tprintf("Callsign loging by source %sabled, ", (axheard_filter_flag & AXHEARD_NOSRC) ? "dis" : "en");
	tprintf("by destination %sabled\n", (axheard_filter_flag & AXHEARD_NODST) ? "dis" : "en");
	return 0;
}

static int
doaxflush(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;

	for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next){
		if(ifp->output != ax_output)
			continue;	/* Not an ax.25 interface */
		axflush(ifp);
	}
	return 0;
}
static void
axflush(ifp)
struct iface *ifp;
{
	struct lq *lp,*lp1;
	struct ld *ld,*ld1;

	ifp->rawsndcnt = 0;
	for(lp = Lq;lp != NULLLQ;lp = lp1){
		lp1 = lp->next;
		free((char *)lp);
	}
	Lq = NULLLQ;
	for(ld = Ld;ld != NULLLD;ld = ld1){
		ld1 = ld->next;
		free((char *)ld);
	}
	Ld = NULLLD;
}

static int
doaxreset(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct ax25_cb *axp;

#ifndef TNOS_68K
	axp = MK_FP(htoi(argv[1]),8);
#else
	axp = htoi(argv[1]);
#endif
	if(!ax25val(axp)){
		tprintf(Notval);
		return 1;
	}
	reset_ax25(axp);
	return 0;
}

/* Display AX.25 link level control blocks */
static int
doaxstat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct ax25_cb *axp;
	char tmp[AXBUF];
    char tmp2[AXBUF];

	if(argc < 2){
#if defined(UNIX) || defined(TNOS_68K)
        tprintf("&AXB     Snd-Q   Rcv-Q   Remote    Local     Iface  State\n");
#else
        tprintf("&AXB Snd-Q   Rcv-Q   Remote    Local     Iface  State\n");
#endif
		for(axp = Ax25_cb;axp != NULLAX25; axp = axp->next){
#if defined(UNIX) || defined(TNOS_68K)
            if(tprintf("%8.8lx %-8d%-8d%-10s%-10s%-7s%s\n",
#else
            if(tprintf("%4.4x %-8d%-8d%-10s%-10s%-7s%s\n",
#endif
                FP_SEG(axp),
				len_q(axp->txq),len_p(axp->rxq),
				pax25(tmp,axp->remote),
                pax25(tmp2,axp->local),
                axp->iface?axp->iface->name:"",
				Ax25states[axp->state]) == EOF)
					return 0;
		}
		return 0;
	}
#ifndef TNOS_68K
    axp = MK_FP(htoi(argv[1]),8);
#else
    axp = htoi(argv[1]);
#endif
    if(!ax25val(axp)){
		tprintf(Notval);
		return 1;
	}
	st_ax25(axp);
	return 0;
}
/* Dump one control block */
void
st_ax25(axp)
register struct ax25_cb *axp;
{
	char tmp[AXBUF];
    char tmp2[AXBUF];

	if(axp == NULLAX25)
		return;
#if defined(UNIX) || defined(TNOS_68K)
    tprintf("&AXB     Local     Remote    Iface  RB V(S) V(R) Unack P Retry State\n");
    tprintf("%8.8lx %-9s %-9s %-6s %c%c",FP_SEG(axp),
#else
    tprintf("&AXB Local     Remote    Iface  RB V(S) V(R) Unack P Retry State\n");
    tprintf("%4.4x %-9s %-9s %-6s %c%c",FP_SEG(axp),
#endif
        pax25(tmp,axp->local),
        pax25(tmp2,axp->remote),
        axp->iface?axp->iface->name:"",
        axp->flags.rejsent ? 'R' : ' ',
        axp->flags.remotebusy ? 'B' : ' ');
	tprintf(" %4d %4d",axp->vs,axp->vr);
	tprintf(" %02u/%02u %u",axp->unack,axp->maxframe,axp->proto);
	tprintf(" %02u/%02u",axp->retries,axp->n2);
	tprintf(" %s\n",Ax25states[axp->state]);
	tprintf("srtt = %lu mdev = %lu ",axp->srt,axp->mdev);
	tprintf("T1: ");
	if(run_timer(&axp->t1))
		tprintf("%lu",read_timer(&axp->t1));
	else
		tprintf("stop");
	tprintf("/%lu ms; ",dur_timer(&axp->t1));

	tprintf("T3: ");
	if(run_timer(&axp->t3))
		tprintf("%lu",read_timer(&axp->t3));
	else
		tprintf("stop");
	tprintf("/%lu ms; ",dur_timer(&axp->t3));

	tprintf("T4: ");
	if(run_timer(&axp->t4))
        tprintf("%lu",(read_timer(&axp->t4)/1000L));
	else
		tprintf("stop");
    tprintf("/%lu sec\n",(dur_timer(&axp->t4)/1000L));
}

/* Set limit on retransmission backoff */
static int
doblimit(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setlong(&Blimit,"blimit",argc,argv);
}

/* Display or change our AX.25 address */
static int
domycall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
#ifdef MAILBOX
    extern void setmbnrid();
#endif
    char tmp[AXBUF];

	if(argc < 2){
		tprintf("%s\n",pax25(tmp,Mycall));
		return 0;
	}
	if(setcall(Mycall,argv[1]) == -1)
		return -1;
#ifdef MAILBOX
    setmbnrid();
#endif
    return 0;
}

#ifdef TTYCALL

/* Display or change ttylink AX.25 address */
static int
dottycall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char tmp[AXBUF];

	if(argc < 2){
		tprintf("%s\n",pax25(tmp,Ttycall));
		return 0;
	}
	if(setcall(Ttycall,argv[1]) == -1)
		return -1;
    return 0;
}

#endif

/* Control AX.25 digipeating */
static int
dodigipeat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return(dosetflag(argc,argv,p,AX25_DIGI, 1));
}

/* Configure a port to do ax.25 heard logging */
static int
doaxhport(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return(dosetflag(argc,argv,p,LOG_AXHEARD, 1));
}

/* Configure a port to do ax.25 beacon broadcasting */
static int
dobcport(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return(dosetflag(argc,argv,p,AX25_BEACON, 1));
}

static int
doaxversion(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&Axversion,"AX25 version",argc,argv);
}

static int
doaxirtt(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setlong(&Axirtt,"Initial RTT (ms)",argc,argv);
}

/* Set idle timer */
static int
dot3(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setlong(&T3init,"Idle poll timer (ms)",argc,argv);
}

/* Set link redundancy timer */
static int
dot4(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setlong(&T4init,"Link redundancy timer (sec)",argc,argv);
}

/* Set retry limit count */
static int
don2(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&N2,"Retry limit",argc,argv);
}
/* Force a retransmission */
static int
doaxkick(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct ax25_cb *axp;

#ifndef TNOS_68K
	axp = MK_FP(htoi(argv[1]),8);
#else
	axp = htoi(argv[1]);
#endif
	if(!ax25val(axp))	{
		tprintf(Notval);
		return 1;
	}
	kick_ax25(axp);
	return 0;
}

/* Set maximum number of frames that will be allowed in flight */
static int
domaxframe(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&Maxframe,"Window size (frames)",argc,argv);
}

/* Set maximum length of I-frame data field */
static int
dopaclen(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&Paclen,"Max frame length (bytes)",argc,argv);
}
/* Set size of I-frame above which polls will be sent after a timeout */
static int
dopthresh(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&Pthresh,"Poll threshold (bytes)",argc,argv);
}

/* Set high water mark on receive queue that triggers RNR */
static int
doaxwindow(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&Axwindow,"AX25 receive window (bytes)",argc,argv);
}
/* End of ax25 subcommands */

/* Initiate interactive AX.25 connect to remote station */
int
doconnect(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct sockaddr_ax fsocket;
	struct session *sp;
	struct iface *ifp;
	int ndigis,i;
	char digis[MAXDIGIS][AXALEN];
	char target[AXALEN];
    int split = 0;
	char tmp[AXBUF];

	/*Make sure this comes from console - WG7J*/
    if(Curproc->input != Command->input)
		return 0;

#ifdef ALLSERV
    if(argv[0][0] == 's')   /* use split screen */
        split  = 1;
#endif

    if(((ifp = if_lookup(argv[1])) != NULLIF) && (ifp->type != CL_AX25)) {
		tprintf("Iface %s not an AX25 type interface\n",argv[1]);
		return 1;
	}

	if(argc > 3 && !strcmp(argv[3], "@"))	{
		free (argv[3]);
		argv[3] = strdup(pax25(tmp,AXRosecall));
	}
	if(setcall(target,argv[2]) == -1){
		tprintf("Bad callsign %s\n", argv[2]);
		return 1;
	}

	/* If digipeaters are given, put them in the routing table */
	if(argc > 3){
		ndigis = argc - 3;
		if(ndigis > MAXDIGIS){
			tprintf("Too many digipeaters\n");
			return 1;
		}
		for(i=0;i<ndigis;i++){
			if(setcall(digis[i],argv[i+3]) == -1){
				tprintf("Bad digipeater %s\n",argv[i+3]);
				return 1;
			}
		}
		if(ax_add(target,AX_AUTO,digis,ndigis,ifp) == NULLAXR){
			tprintf("Route add failed\n");
			return 1;
		}
	}
	/* Allocate a session descriptor */
	if((sp = newsession(argv[2],AX25TNC,split)) == NULLSESSION){
        tputs(TooManySessions);
		return 1;
	}
	if((sp->s = socket(AF_AX25,SOCK_STREAM,0)) == -1){
        tputs(Nosock);
		freesession(sp);
		keywait(NULLCHAR,1);
		return 1;
	}
	fsocket.sax_family = AF_AX25;
	setcall(fsocket.ax25_addr,argv[2]);
	strncpy(fsocket.iface,argv[1],ILEN);
	return tel_connect(sp, (char *)&fsocket, sizeof(struct sockaddr_ax));
}

/* Display and modify AX.25 routing table */
static int
doaxroute(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	char tmp[AXBUF];
	int i,ndigis;
	register struct ax_route *axr;
	char target[AXALEN],digis[MAXDIGIS][AXALEN];
	struct iface *iface;

	if(argc < 2){
		tputs("Target    Iface  Type  Mode Digipeaters\n");
		for(axr = Ax_routes;axr != NULLAXR;axr = axr->next){
			tprintf("%-10s%-7s%-6s",pax25(tmp,axr->target),
				axr->iface->name,axr->type == AX_LOCAL ? "Local":"Auto");
			switch(axr->mode){
			case AX_VC_MODE:
				tputs(" VC ");
				break;
			case AX_DATMODE:
				tputs(" DG ");
				break;
			case AX_DEFMODE:
				tputs(" IF ");
				break;
			default:
				tputs(" ?? ");
				break;
			}

			for(i=0;i<axr->ndigis;i++){
				tprintf(" %s",pax25(tmp,axr->digis[i]));
			}
			if(tprintf("\n") == EOF)
				return 0;
		}
		return 0;
	}
	if(argc < 4){
		tprintf("Usage: ax25 route add <target> <iface> [digis...]\n");
		tprintf("       ax25 route drop <target> <iface>\n");
		tprintf("       ax25 route mode <target> <iface> [mode]\n");
		return 1;
	}
	if(setcall(target,argv[2]) == -1){
		tprintf("Bad target %s\n",argv[2]);
		return 1;
	}
	if((iface = if_lookup(argv[3])) == NULLIF){
		tprintf(Badinterface,argv[3]);
		return 1;
	}
	switch(argv[1][0]){
	case 'a':	/* Add route */
		ndigis = argc - 4;
		if(ndigis > MAXDIGIS){
			tprintf("Too many digipeaters\n");
			return 1;
		}
		for(i=0;i<ndigis;i++){
			if(setcall(digis[i],argv[i+4]) == -1){
				tprintf("Bad digipeater %s\n",argv[i+4]);
				return 1;
			}
		}
		if(ax_add(target,AX_LOCAL,digis,ndigis,iface) == NULLAXR){
			tprintf("Failed\n");
			return 1;
		}
		break;
	case 'd':	/* Drop route */
		if(ax_drop(target,iface) == -1){
			tprintf("Not in table\n");
			return 1;
		}
		break;
	case 'm':	/* Alter route mode */
		if((axr = ax_lookup(target,iface)) == NULLAXR){
			tprintf("Not in table\n");
			return 1;
		}
		switch(argv[4][0]){
		case 'i':	/* use default interface mode */
			axr->mode = AX_DEFMODE;
			break;
		case 'v':	/* use virtual circuit mode */
			axr->mode = AX_VC_MODE;
			break;
		case 'd':	/* use datagram mode */
			axr->mode = AX_DATMODE;
			break;
		default:
			tprintf("Unknown mode %s\n", argv[4]);
			return 1;
		}
		break;
	default:
		tprintf("Unknown command %s\n",argv[1]);
		return 1;
	}
	return 0;
}

/* ax25 timers type - linear v exponential */
static int
doaxtype(argc,argv,p)
int argc ;
char *argv[] ;
void *p ;
{
	extern int lapbtimertype;

	if (argc < 2) {
		tprintf("AX25 timer type is ");
		switch(lapbtimertype){
		case 2:
			tprintf("original\n");
			break;
		case 1:
			tprintf("linear\n");
			break;
		case 0:
			tprintf("exponential\n");
			break;
		}
		return 0 ;
	}
	
	switch (argv[1][0]) {
		case 'o':
		case 'O':
			lapbtimertype = 2 ;
			break ;
		case 'l':
		case 'L':
			lapbtimertype = 1 ;
			break ;
		case 'e':
		case 'E':
			lapbtimertype = 0 ;
			break ;
		default:
			tprintf("use: ax25 timertype [original|linear|exponential]\n") ;
			return -1 ;
	}

	return 0 ;
}

#ifdef TUTOR
/* Display or change an AX.25 tutorial-type call */
static int
docall(argc,argv,p, call)
int argc;
char *argv[];
void *p;
char call[];
{
char tmp[AXBUF];

	if(argc < 2)	{
		tprintf("%s\n",pax25(tmp,call));
		return 0;
	}
	if(setcall(call,argv[1]) == -1)
		return -1;
	return 0;
}


/* Display or change our AX.25 rose entry call */
static int
dorosecall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (docall (argc, argv, p, AXRosecall));
}


/* Display or change our AX.25 tutorial call */
static int
dotutorcall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (docall (argc, argv, p, Tcall));
}


/* Display or change our AX.25 info call */
static int
doinfocall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (docall (argc, argv, p, Icall));
}


/* Display or change our AX.25 news call */
static int
donewscall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (docall (argc, argv, p, Ncall));
}
#endif

