/* IP-related user commands */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "internet.h"
#include "timer.h"
#include "netuser.h"
#include "iface.h"
#include "ip.h"
#include "cmdparse.h"

int doipaddr(),doipstat(),dottl();
extern char badhost[];
struct cmds ipcmds[] = {
	"address",	doipaddr,	0,	NULLCHAR,	NULLCHAR,
	"status",	doipstat,	0,	NULLCHAR,	NULLCHAR,
	"ttl",		dottl,		0,	NULLCHAR,	NULLCHAR,
	NULLCHAR,	NULLFP,		0,
		"?subcommands", NULLCHAR
};
doip(argc,argv)
int argc;
char *argv[];
{
	return subcmd(ipcmds,argc,argv);
}
int
doipaddr(argc,argv)
int argc;
char *argv[];
{
	int32 n;

	if(argc < 2) {
		printf("%s\n",inet_ntoa(ip_addr));
	} else if((n = resolve(argv[1])) == 0){
		printf(badhost,argv[1]);
		return 1;
	} else
		ip_addr = n;
	return 0;
}
int
dottl(argc,argv)
char *argv[];
{
	if(argc < 2)
		printf("%u\n",uchar(ip_ttl));
	else
		ip_ttl = atoi(argv[1]);
	return 0;
}

/* "route" subcommands */
int doadd(),dodrop(),doinfo();
static void dump1route();
static struct cmds rtcmds[] = {
	"add", doadd, 3,
	"<dest addr>[/<bits>] <if name>|via [gateway] [metric]",
	"Add failed",

	"drop", dodrop, 2,
	"<dest addr>[/<bits>]",
	"Not in table",

	"info", doinfo, 2,
	"<dest addr>",
	NULLCHAR,

	NULLCHAR, NULLFP, 0,
	"?subcommands",
	NULLCHAR
};

/* Display and/or manipulate routing table */
int
doroute(argc,argv)
int argc;
char *argv[];
{
	if(argc < 2){
		dumproute();
		return 0;
	}
	return subcmd(rtcmds,argc,argv);
}
/* Add an entry to the routing table
 * E.g., "add 1.2.3.4 ax0 5.6.7.8 3"
 */
int
doadd(argc,argv)
int argc;
char *argv[];
{
	struct interface *ifp;
	int32 dest;
	int32 gateway = 0;
	unsigned bits;
	char *bitp;
	int metric = 0;
	struct route *rp,*rt_lookup();

	if(strcmp(argv[1],"default") == 0){
		dest = 0;
		bits = 0;
	} else {
		/* If IP address is followed by an optional slash and
		 * a length field, (e.g., 128.96/16) get it;
		 * otherwise assume a full 32-bit address
		 */
		if((bitp = index(argv[1],'/')) != NULLCHAR){
			*bitp++ = '\0';
			bits = atoi(bitp);
		} else
			bits = 32;

		if((dest = resolve(argv[1])) == 0){
			printf(badhost,argv[1]);
			return 1;
		}
	}
	if(strlen(argv[2]) >= 3 && strncmp(argv[2],"unreachable",strlen(argv[2])) == 0){
		return rt_add(dest,bits,gateway,metric,NULLIF);
	}
	if(argc > 3){
		if((gateway = resolve(argv[3])) == 0){
			printf(badhost,argv[3]);
			return 1;
		}
		if(strcmp(argv[2],"via")){
			if((ifp = ifunit(argv[2])) == NULLIF){
				return 1;
			}
		} else {
			if((rp = rt_lookup(gateway)) == NULLROUTE){
				printf("No route to %s\n",argv[3]);
				return 1;
			}
			if(rp->gateway)
				gateway = rp->gateway;

			ifp = rp->interface;
		}
	} else {
		if((ifp = ifunit(argv[2])) == NULLIF){
			return 1;
		}
	}
	if(argc > 4)
		metric = atoi(argv[4]);

	return rt_add(dest,bits,gateway,metric,ifp);
}
/* Drop an entry from the routing table
 * E.g., "drop 128.96/16
 */
int
dodrop(argc,argv)
int argc;
char *argv[];
{
	char *bitp;
	unsigned bits;
	int32 n;

	if(strcmp(argv[1],"default") == 0){
		n = 0;
		bits = 0;
	} else {
		/* If IP address is followed by an optional slash and length field,
		 * (e.g., 128.96/16) get it; otherwise assume a full 32-bit address
		 */
		if((bitp = index(argv[1],'/')) != NULLCHAR){
			*bitp++ = '\0';
			bits = atoi(bitp);
		} else
			bits = 32;

		if((n = resolve(argv[1])) == 0){
			printf(badhost,argv[1]);
			return 1;
		}
	}
	return rt_drop(n,bits);
}
/* 
 * Display route to a certain IP address
 */
int
doinfo(argc,argv)
int argc;
char *argv[];
{
	int32 dest;
	struct route *rp,*rt_lookup();
	
	if((dest = resolve(argv[1])) == 0){
		printf(badhost,argv[1]);
		return 1;
	}

	if((rp = rt_lookup(dest)) == NULLROUTE){
		printf("No route to %s\n",argv[1]);
		return 1;
	}

	printf("%s: ",inet_ntoa(dest));
	dump1route(rp);
	return 0;
}

/* Dump IP routing table
 * Dest		     Length    Interface    Gateway	     Metric
 * 192.001.002.003   32	       sl0	    192.002.003.004	  4
 */
int
dumproute()
{
	register unsigned int i,bits;
	register struct route *rp;

	printf("Dest              Length    Interface    Gateway          Metric\n");
	for(bits=0;bits<=32;bits++){
		for(i=0;i<NROUTE;i++){
			for(rp = routes[bits][i];rp != NULLROUTE;rp = rp->next){
				printf("%-18s",(bits == 0)?"default":inet_ntoa(rp->target));
				printf("%-10u",bits);
				dump1route(rp);
			}
		}
	}
	return 0;
}

static void
dump1route (rp)
register struct route *rp;

{
	if(rp->interface == NULLIF){
		printf("unreachable\n");
	} else {
		printf("%-13s",rp->interface->name);
		printf("%-17s",(rp->gateway != 0)? inet_ntoa(rp->gateway):"");
		printf("%6u\n",rp->metric);
	}
}

int
doipstat(argc,argv)
int argc;
char *argv[];
{
	extern struct ip_stats ip_stats;
	extern struct reasm *reasmq;
	register struct reasm *rp;
	register struct frag *fp;

	printf("IP: total %ld runt %u len err %u vers err %u",
		ip_stats.total,ip_stats.runt,ip_stats.length,ip_stats.version);
	printf(" chksum err %u badproto %u\n",
		ip_stats.checksum,ip_stats.badproto);

	if(reasmq != NULLREASM)
		printf("Reassembly fragments:\n");
	for(rp = reasmq;rp != NULLREASM;rp = rp->next){
		printf("src %s",inet_ntoa(rp->source));
		printf(" dest %s",inet_ntoa(rp->dest));
		printf(" id %u pctl %u time %lu len %u\n",
			rp->id,uchar(rp->protocol),rp->timer.count,rp->length);
		for(fp = rp->fraglist;fp != NULLFRAG;fp = fp->next){
			printf(" offset %u last %u\n",fp->offset,fp->last);
		}
	}
	return doicmpstat();
}
