/* IP interface control and configuration routines
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Mods by PA0GRI 
 */
#include <stdio.h>
#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "ip.h"
#include "netuser.h"
#include "ax25.h"
#ifdef ETHER
#include "enet.h"
#endif
#include "arp.h"
#include "pktdrvr.h"
#include "cmdparse.h"
#include "commands.h"
#include "nr4.h"
#include "socket.h"
#include "mailbox.h" 

#ifdef NETROM
extern struct iface *Nr_iface;
#endif

static void showiface __ARGS((struct iface *ifp));
static int mask2width __ARGS((int32 mask));
static int ifipaddr __ARGS((int argc,char *argv[],void *p));
static int ifipcall __ARGS((int argc,char *argv[],void *p));
static int iflinkadr __ARGS((int argc,char *argv[],void *p));
static int ifbroad __ARGS((int argc,char *argv[],void *p));
static int ifnetmsk __ARGS((int argc,char *argv[],void *p));
static int ifrxbuf __ARGS((int argc,char *argv[],void *p));
static int ifmtu __ARGS((int argc,char *argv[],void *p));
static int ifforw __ARGS((int argc,char *argv[],void *p));
static int ifencap __ARGS((int argc,char *argv[],void *p));
static int ifdescr __ARGS((int argc,char *argv[],void *p));
static int ifpaclen __ARGS((int argc,char *argv[],void *p));

/* Interface list header */
struct iface *Ifaces = &Loopback;

/* Loopback pseudo-interface */
struct iface Loopback = {
#ifdef ENCAP
    &Encap,     /* Link to next entry */
#else
    NULLIF,
#endif
    "loopback", /* name     */
    NULLCHAR,   /* description */
	0x7f000001L,	/* addr		127.0.0.1 */
	-1L,		/* broadcast	255.255.255.255 */
	-1L,		/* netmask	255.255.255.255 */
	MAXINT16,	/* mtu		No limit */
    0,          /* AX.25 paclen, if applicable */
    0,      /* flags    */
    0,      /* quality  */
	0,		/* trace	*/
	NULLCHAR,	/* trfile	*/
	NULLFILE,	/* trfp		*/
	NULLIF,		/* forw		*/
	NULLPROC,	/* rxproc	*/
	NULLPROC,	/* txproc	*/
	NULLPROC,	/* supv		*/
	0,		/* dev		*/
	(int32(*)__FARGS((struct iface*,int,int,int32)))NULL,/* (*ioctl)     */
	NULLFP((struct iface*,int,int32)),         /* (*iostatus)  */
	NULLFP((struct iface*)),         /* (*stop)      */
	NULLCHAR,	/* hwaddr	*/
	NULLCHAR,	/* ipcall	*/
	NULL,		/* extension	*/
	CL_NONE,	/* type		*/
	0,		/* xdev		*/
	0,		/* port		*/
	&Iftypes[0],	/* iftype	*/
	NULLFP((struct mbuf*,struct iface*,int32,int,int,int,int)),/* (*send)      */
	NULLFP((struct iface*,char*,char*,int16,struct mbuf*)),/* (*output)    */
	NULLFP((struct iface*,struct mbuf*)),         /* (*raw)       */
	NULLVFP((struct iface*)),        /* (*show)      */
	NULLFP((struct iface*,struct mbuf*)),         /* (*discard)   */
	NULLFP((struct iface*,struct mbuf*)),         /* (*echo)      */
	0,		/* ipsndcnt	*/
	0,		/* rawsndcnt	*/
	0,		/* iprecvcnt	*/
	0,		/* rawrcvcnt	*/
	0,		/* lastsent	*/
	0,		/* lastrecv	*/
};

#ifdef ENCAP
/* Encapsulation pseudo-interface */
struct iface Encap = {
	NULLIF,
	"encap",	/* name		*/
    NULLCHAR,   /* description */
	INADDR_ANY,	/* addr		0.0.0.0 */
	-1L,		/* broadcast	255.255.255.255 */
	-1L,	/* netmask	255.255.255.255 */
	MAXINT16,	/* mtu		No limit */
    0,          /* AX.25 paclen, if applicable */
    0,      /* flags    */
    0,      /* quality  */
	0,		/* trace	*/
	NULLCHAR,	/* trfile	*/
	NULLFILE,	/* trfp		*/
	NULLIF,		/* forw		*/
	NULLPROC,	/* rxproc	*/
	NULLPROC,	/* txproc	*/
	NULLPROC,	/* supv		*/
	0,		/* dev		*/
	(int32(*)__FARGS((struct iface*,int,int,int32)))NULL,/* (*ioctl)     */
	NULLFP((struct iface*,int,int32)),         /* (*iostatus)  */
	NULLFP((struct iface*)),         /* (*stop)      */
	NULLCHAR,	/* hwaddr	*/
	NULLCHAR,	/* ipcall	*/
	NULL,		/* extension	*/
	CL_NONE,	/* type		*/
	0,		/* xdev		*/
	0,		/* port		*/
	&Iftypes[0],	/* iftype	*/
	ip_encap,	/* (*send)	*/
	NULLFP((struct iface*,char*,char*,int16,struct mbuf*)),/* (*output)    */
	NULLFP((struct iface*,struct mbuf*)),         /* (*raw)       */
	NULLVFP((struct iface*)),        /* (*show)      */
	NULLFP((struct iface*,struct mbuf*)),         /* (*discard)   */
	NULLFP((struct iface*,struct mbuf*)),         /* (*echo)      */
	0,		/* ipsndcnt	*/
	0,		/* rawsndcnt	*/
	0,		/* iprecvcnt	*/
	0,		/* rawrcvcnt	*/
	0,		/* lastsent	*/
	0,		/* lastrecv	*/
};
#endif /*ENCAP*/

char Noipaddr[] = "IP address field missing, and ip address not set\n";

struct cmds DFAR Ifcmds[] = {
	"broadcast",		ifbroad,	0,	2,	NULLCHAR,
    "description",      ifdescr,    0,  0,  NULLCHAR,
	"encapsulation",	ifencap,	0,	2,	NULLCHAR,
	"forward",		ifforw,		0,	2,	NULLCHAR,
	"ipaddress",		ifipaddr,	0,	2,	NULLCHAR,
	"ipcall",		ifipcall,	0,	2,	NULLCHAR,
	"linkaddress",		iflinkadr,	0,	2,	NULLCHAR,
	"mtu",			ifmtu,		0,	2,	NULLCHAR,
	"netmask",		ifnetmsk,	0,	2,	NULLCHAR,
#ifdef AX25
    "paclen",       ifpaclen,   0,  2,  NULLCHAR,
#endif
    "rxbuf",        ifrxbuf,    0,  2,  NULLCHAR,
	NULLCHAR,
};

/* Set interface parameters */
int
doifconfig(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;
	int i;

	if(argc < 2){
		for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next)
			showiface(ifp);
		return 0;
	}
	if((ifp = if_lookup(argv[1])) == NULLIF){
        tprintf(Badinterface,argv[1]);
		return 1;
	}
	if(argc == 2){
		showiface(ifp);
		if ( ifp->show != NULLVFP((struct iface*)) ) {
			(*ifp->show)(ifp);
		}
		return 0;
	}
	if(argc == 3){
		dohelper ("valid subcommands:\n", Ifcmds, NULLCHAR, NULLCHAR, NULLCHAR);
		return 1;
	}
	for(i=2;i<argc-1;i+=2)
		subcmd(Ifcmds,3,&argv[i-1],ifp);

	return 0;
}

/* Set interface IP address */
static int
ifipaddr(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;

    /* Do not allow loopback iface to be changed ! - WG7J */
    if(ifp == &Loopback) {
        tputs("Cannot change IP address !\n");
        return 0;
    }
	ifp->addr = resolve(argv[1]);
	return 0;
}


/* Set IP call sign */
static int
ifipcall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;

	if(ifp->iftype == NULLIFT || ifp->iftype->scan == NULL){
		tprintf("Can't set IP call\n");
		return 1;
	}
	if(ifp->ipcall != NULLCHAR)
		free(ifp->ipcall);
	ifp->ipcall = mallocw((unsigned)ifp->iftype->hwalen);
	(*ifp->iftype->scan)(ifp->ipcall,argv[1]);
	return 0;
}

/* Set link (hardware) address */
static int
iflinkadr(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;

	if(ifp->iftype == NULLIFT || ifp->iftype->scan == NULL){
		tprintf("Can't set link address\n");
		return 1;
	}
	if(ifp->hwaddr != NULLCHAR)
		free(ifp->hwaddr);
	ifp->hwaddr = mallocw((unsigned)ifp->iftype->hwalen);
	(*ifp->iftype->scan)(ifp->hwaddr,argv[1]);
#ifdef MAILBOX
#ifdef NETROM
    if(ifp == Nr_iface) /*the netrom call just got changed! - WG7J*/
        setmbnrid();
#endif
#endif
    return 0;
}

/* Set interface broadcast address. This is actually done
 * by installing a private entry in the routing table.
 */
static int
ifbroad(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;
	struct route *rp;

	rp = rt_blookup(ifp->broadcast,32);
	if(rp != NULLROUTE && rp->iface == ifp)
		rt_drop(ifp->broadcast,32);
	ifp->broadcast = resolve(argv[1]);
	rt_add(ifp->broadcast,32,0L,ifp,1L,0L,1);
	return 0;
}

/* Set the network mask. This is actually done by installing
 * a routing entry.
 */
static int
ifnetmsk(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;
	struct route *rp;

	/* Remove old entry if it exists */
	rp = rt_blookup(ifp->addr & ifp->netmask,(unsigned)mask2width(ifp->netmask));
	if(rp != NULLROUTE)
		rt_drop(rp->target,rp->bits);

	ifp->netmask = htol(argv[1]);
	rt_add(ifp->addr,(unsigned)mask2width(ifp->netmask),0L,ifp,0L,0L,0);
	return 0;
}

/* Command to set interface encapsulation mode */
static int
ifencap(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;

	if(setencap(ifp,argv[1]) != 0){
		tprintf("Encapsulation mode '%s' unknown\n",argv[1]);
		return 1;
	}
	return 0;
}
/* Function to set encapsulation mode */
int
setencap(ifp,mode)
struct iface *ifp;
char *mode;
{
	struct iftype *ift;

	for(ift = &Iftypes[0];ift->name != NULLCHAR;ift++)
		if(strnicmp(ift->name,mode,strlen(mode)) == 0)
			break;
	if(ift->name == NULLCHAR){
		return -1;
	}
	ifp->iftype = ift;
	ifp->send = ift->send;
	ifp->output = ift->output;
	ifp->type = ift->type;
#ifdef AX25
    if(ift->type == CL_AX25)	{
        ifp->paclen = Paclen; /* default to the 'ax25 paclen' value */
	ifp->flags += AX25_BEACON+MAIL_BEACON+AX25_DIGI+LOG_AXHEARD+LOG_IPHEARD;
    }
#endif
    return 0;
}
/* Set interface receive buffer size */
static int
ifrxbuf(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return 0;	/* To be written */
}

/* Set interface Maximum Transmission Unit */
static int
ifmtu(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;

	ifp->mtu = atoi(argv[1]);
#ifdef NETROM
    /* Make sure NETROM mtu <= 236 ! - WG7J */
    if(ifp == Nr_iface)
        if(Nr_iface->mtu > NR4MAXINFO)
            Nr_iface->mtu = NR4MAXINFO;
#endif
    return 0;
}

#ifdef AX25

/* Set interface AX.25 Paclen, and adjust the NETROM mtu for the
 * smallest paclen. - WG7J
 */
static int
ifpaclen(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;
#ifdef NETROM
    int tmp;
#endif

    if(ifp->iftype->type == CL_AX25) {
        ifp->paclen = atoi(argv[1]);
#ifdef NETROM
        if(ifp->flags & IS_NR_IFACE)
            if((tmp=ifp->paclen - 20) < Nr_iface->mtu)
                Nr_iface->mtu = tmp;
#endif
    } else
        tputs("Not ax.25 interface!\n");
    return 0;
}
#endif

/* Set interface forwarding */
static int
ifforw(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp = p;

	ifp->forw = if_lookup(argv[1]);
	if(ifp->forw == ifp)
		ifp->forw = NULLIF;
	return 0;
}

/*give a little description for each interface - WG7J*/
static int
ifdescr(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct iface *ifp = p;

#ifdef NETROM
#ifdef ENCAP
    if((ifp == &Loopback) || (ifp == &Encap) || (ifp == Nr_iface))
#else
    if((ifp == &Loopback) || (ifp == Nr_iface))
#endif /*ENCAP*/
#else  /*NETROM*/
#ifdef ENCAP
    if((ifp == &Loopback) || (ifp == &Encap))
#else
    if(ifp == &Loopback)
#endif /*ENCAP*/
#endif /*NETROM*/
        return 0;

    if(ifp->descr != NULLCHAR){
        free(ifp->descr);
        ifp->descr = NULLCHAR;        /* reset the pointer */
    }
    if(!strlen(argv[1]))
        return 0;           /* clearing the buffer */

    ifp->descr = mallocw(strlen(argv[1])+5); /* allow for the EOL char etc */
    strcpy(ifp->descr, argv[1]);
    strcat(ifp->descr, "\n");         /* add the EOL char */

	return 0;
}



/* Display the parameters for a specified interface */
static void
showiface(ifp)
register struct iface *ifp;
{
	char tmp[25];

    tprintf("%-10s IP addr %s MTU %u Link encap ",ifp->name,
     inet_ntoa(ifp->addr),(int)ifp->mtu);
	if(ifp->iftype == NULLIFT){
		tprintf("not set\n");
	} else {
		tprintf("%s\n",ifp->iftype->name);
        if(ifp->iftype->format != NULL && ifp->hwaddr != NULLCHAR) {
            tprintf("           Link addr %s",
			 (*ifp->iftype->format)(tmp,ifp->hwaddr));
            if(ifp->iftype->type == CL_AX25)
                tprintf("   Paclen %d",(int)ifp->paclen);
            if(ifp->ipcall != NULLCHAR)
                tprintf("   IP call %s", (*ifp->iftype->format)(tmp,ifp->ipcall));
            tputc('\n');
        }
	}
        tprintf("           flags 0x%lx trace 0x%x netmask 0x%08lx broadcast %s\n",
		ifp->flags,ifp->trace,ifp->netmask,inet_ntoa(ifp->broadcast));
	if(ifp->forw != NULLIF)
		tprintf("           output forward to %s\n",ifp->forw->name);
	tprintf("           sent: ip %lu tot %lu idle %s\n",
	 ifp->ipsndcnt,ifp->rawsndcnt,tformat(secclock() - ifp->lastsent));
	tprintf("           recv: ip %lu tot %lu idle %s\n",
	 ifp->iprecvcnt,ifp->rawrecvcnt,tformat(secclock() - ifp->lastrecv));
    if(ifp->descr != NULLCHAR)
        tprintf("           descr: %s",ifp->descr);

}

/* Command to detach an interface */
int
dodetach(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct iface *ifp;

	if((ifp = if_lookup(argv[1])) == NULLIF){
        tprintf(Badinterface,argv[1]);
		return 1;
	}
	if(if_detach(ifp) == -1)
		tprintf("Can't detach loopback or encap interface\n");
	return 0;
}
/* Detach a specified interface */
int
if_detach(ifp)
register struct iface *ifp;
{
	struct iface *iftmp;
	struct route *rp,*rptmp;
    int i,j;
    struct ax_route *axr, *axr1;
    struct arp_tab *ap, *ap1;

#ifdef ENCAP
	if(ifp == &Loopback || ifp == &Encap)
#else
    if(ifp == &Loopback)
#endif
            return -1;

#ifdef AX25
	/* Drop all ax25 routes that points to this interface */
	for(axr = Ax_routes; axr != NULLAXR; axr = axr1) {
		axr1 = axr->next;/* Save the next pointer */
		if(axr->iface == ifp)
            ax_drop(axr->target, ifp);
		/* axr will be undefined after ax_drop() */
	}
#endif

	/* Drop all ARP's that point to this interface */
	for(i = 0; i < HASHMOD; ++i)
	    for(ap = Arp_tab[i]; ap != NULLARP; ap = ap1) {
		ap1 = ap->next;	/* Save the next pointer */
		if(ap->iface == ifp)
			arp_drop(ap);
		/* ap will be undefined after arp_drop() */
	    }


	/* Drop all routes that point to this interface */
	if(R_default.iface == ifp)
		rt_drop(0L,0);	/* Drop default route */

	for(i=0;i<HASHMOD;i++){
		for(j=0;j<32;j++){
			for(rp = Routes[j][i];rp != NULLROUTE;rp = rptmp){
				/* Save next pointer in case we delete this entry */
				rptmp = rp->next;
				if(rp->iface == ifp)
					rt_drop(rp->target,rp->bits);
			}
		}
	}
	/* Unforward any other interfaces forwarding to this one */
	for(iftmp = Ifaces;iftmp != NULLIF;iftmp = iftmp->next){
		if(iftmp->forw == ifp)
			iftmp->forw = NULLIF;
	}

	/* Call device shutdown routine, if any */
	if(ifp->stop != NULLFP((struct iface*)))
		(*ifp->stop)(ifp);

	killproc(ifp->rxproc);
	killproc(ifp->txproc);
	killproc(ifp->supv);

#ifdef NETROM
    /*make sure that the netrom interface is properly detached
     *fixed 11/15/91, Johan. K. Reinalda, WG7J/PA3DIS
     */
    if(strcmp(ifp->name,"netrom") == 0) {
        for(iftmp=Ifaces;iftmp;iftmp=iftmp->next)
            iftmp->flags &= ~IS_NR_IFACE; /* this 'resets' active interfaces */
        Nr_iface = NULLIF;
    }
#endif

	/* Free allocated memory associated with this interface */
	if(ifp->name != NULLCHAR)
		free(ifp->name);
	if(ifp->hwaddr != NULLCHAR)
		free(ifp->hwaddr);
	if(ifp->ipcall != NULLCHAR)
		free(ifp->ipcall);

	/* Remove from interface list */
	if(ifp == Ifaces){
		Ifaces = ifp->next;
	} else {
		/* Search for entry just before this one
		 * (necessary because list is only singly-linked.)
		 */
		for(iftmp = Ifaces;iftmp != NULLIF ;iftmp = iftmp->next)
			if(iftmp->next == ifp)
				break;
		if(iftmp != NULLIF && iftmp->next == ifp)
			iftmp->next = ifp->next;
	}
	/* Finally free the structure itself */
	free((char *)ifp);
	return 0;
}

/* Given the ascii name of an interface, return a pointer to the structure,
 * or NULLIF if it doesn't exist
 */
struct iface *
if_lookup(name)
char *name;
{
	register struct iface *ifp;

	for(ifp = Ifaces; ifp != NULLIF; ifp = ifp->next)
		if(stricmp(ifp->name,name) == 0)
			break;
	return ifp;
}

/* Return iface pointer if 'addr' belongs to one of our interfaces,
 * NULLIF otherwise.
 * This is used to tell if an incoming IP datagram is for us, or if it
 * has to be routed.
 */
struct iface *
ismyaddr(addr)
int32 addr;
{
	register struct iface *ifp;

	for(ifp = Ifaces; ifp != NULLIF; ifp = ifp->next)
		if(addr == ifp->addr)
			break;
	return ifp;
}

/* Given a network mask, return the number of contiguous 1-bits starting
 * from the most significant bit.
 */
static int
mask2width(mask)
int32 mask;
{
	int width,i;

	width = 0;
	for(i = 31;i >= 0;i--){
		if(!(mask & (1L << i)))
			break;
		width++;
	}
	return width;
}

/* return buffer with name + comment */
char *
if_name(ifp,comment)
struct iface *ifp;
char *comment;
{
	char *result = mallocw( strlen(ifp->name) + strlen(comment) + 1 );
	strcpy( result, ifp->name );
	return strcat( result, comment );
}

/* Raw output routine that tosses all packets. Used by dialer, tip, etc */
int
bitbucket(ifp,bp)
struct iface *ifp;
struct mbuf *bp;
{
	free_p(bp);
	return 0;
}

/* Set interface (or AX25) flags on 1 or all interfaces */
int
dosetflag(argc,argv,p,flagtoset,AX25only)
int argc;
char *argv[];
void *p;
int flagtoset, AX25only;
{
struct iface *ifp;

	if(argc < 2) {
		for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next)
			if(ifp->flags & flagtoset)
		tprintf("%s\n",ifp->name);
	} else {
		if((argc == 3) && (strcmp(argv[1],"all") == 0)) {
			for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next)
				if(!AX25only || ifp->type == CL_AX25) {
					if(strcmp(argv[2],"on") == 0)
						ifp->flags |= flagtoset;
					else if(strcmp(argv[2],"off") == 0)
						ifp->flags &= ~flagtoset;
					else {
						tprintf("Please use on or off.\n");
						return 0;
					}
				}
			return 0;
		} else if((ifp = if_lookup(argv[1])) == NULLIF){
			tprintf(Badinterface,argv[1]);
			return 1;
		}
		if(AX25only && ifp->type != CL_AX25) {
			tprintf("not an AX.25 interface\n");
			return -1;
		}
		ifp->flags ^= flagtoset;
	}
	return 0;
}


