#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "ax25.h"
#include "timer.h"
#include "lapb.h"
#include "ip.h"
#include "trace.h"

extern FILE *trfp;

/* Dump an AX.25 packet header */
ax25_dump(bpp)
struct mbuf **bpp;
{
	char *decode_type();
	char tmp[20];
	char control,pid;
	int16 type,ftype();
	struct ax25 hdr;
	struct ax25_addr *hp;

	fprintf(trfp,"AX25: ");
	/* Extract the address header */
	if(ntohax25(&hdr,bpp) < 0){
		/* Something wrong with the header */
		fprintf(trfp,"bad header!\n");
		return;
	}
	pax25(tmp,&hdr.source);
	fprintf(trfp,"%s",tmp);
	pax25(tmp,&hdr.dest);
	fprintf(trfp,"->%s",tmp);
	if(hdr.ndigis > 0){
		fprintf(trfp," v");
		for(hp = &hdr.digis[0]; hp < &hdr.digis[hdr.ndigis]; hp++){
			/* Print digi string */
			pax25(tmp,hp);
			fprintf(trfp," %s%s",tmp,(hp->ssid & REPEATED) ? "*":"");
		}
	}
	if(pullup(bpp,&control,1) != 1){
		fprintf(trfp," no control!\n");
		return;
	}

	type = ftype(control);
	fprintf(trfp," %s",decode_type(type));
	/* Dump poll/final bit */
	if(control & PF){
		switch(hdr.cmdrsp){
		case COMMAND:
			fprintf(trfp,"(P)");
			break;
		case RESPONSE:
			fprintf(trfp,"(F)");
			break;
		default:
			fprintf(trfp,"(P/F)");
			break;
		}
	}
	/* Dump sequence numbers */
	if((type & 0x3) != U)	/* I or S frame? */
		fprintf(trfp," NR=%d",(control>>5)&7);
	if(type == I || type == UI){
		if(type == I)
			fprintf(trfp," NS=%d",(control>>1)&7);
		/* Decode I field */
		if(pullup(bpp,&pid,1) == 1){	/* Get pid */
			switch(pid & (PID_FIRST | PID_LAST)){
			case PID_FIRST:
				fprintf(trfp," First frag");
				break;
			case PID_LAST:
				fprintf(trfp," Last frag");
				break;
			case PID_FIRST|PID_LAST:
				break;	/* Complete message, say nothing */
			case 0:
				fprintf(trfp," Middle frag");
				break;
			}
			fprintf(trfp," pid=");
			switch(pid & 0x3f){
			case PID_ARP:
				fprintf(trfp,"ARP\n");
				break;
			case PID_NETROM:
				fprintf(trfp,"NET/ROM\n");
				break;
			case PID_IP:
				fprintf(trfp,"IP\n");
				break;
			case PID_NO_L3:
				fprintf(trfp,"Text\n");
				break;
			default:
				fprintf(trfp,"0x%x\n",uchar(pid));
			}
			/* Only decode frames that are the first in a
			 * multi-frame sequence
			 */
			switch(pid & (PID_PID | PID_FIRST)){
			case PID_ARP | PID_FIRST:
				arp_dump(bpp);
				break;
			case PID_IP | PID_FIRST:
				/* Only checksum complete frames */
				ip_dump(bpp,pid & PID_LAST);
				break;
			case PID_NETROM | PID_FIRST:
				netrom_dump(bpp);
				break;
			}
			return;
		}
	} else if(type == FRMR && pullup(bpp,tmp,3) == 3){
		fprintf(trfp,": %s Vr = %d Vs = %d",decode_type(ftype(tmp[0])),
				(tmp[1] >> 5) & MMASK,(tmp[1] >> 1) & MMASK);
		if(tmp[2] & W)
			fprintf(trfp," Invalid control field");
		if(tmp[2] & X)
			fprintf(trfp," Illegal I-field");
		if(tmp[2] & Y)
			fprintf(trfp," Too-long I-field");
		if(tmp[2] & Z)
			fprintf(trfp," Invalid seq number");
	}
	fprintf(trfp,"\n");
}
/* Display NET/ROM network and transport headers */
static
netrom_dump(bpp)
struct mbuf **bpp;
{
	struct ax25_addr src,dest;
	char x;
	char tmp[16];
	register int i;

	if(bpp == NULLBUFP || *bpp == NULLBUF)
		return;
	/* See if it is a routing broadcast */
	if(uchar(*(*bpp)->data) == 0xff) {
		pullup(bpp,tmp,1);		/* Signature */
		pullup(bpp,tmp,ALEN);
		tmp[ALEN] = '\0';
		fprintf(trfp,"NET/ROM Routing: %s\n",tmp);
		for(i = 0;i < 11;i++) {
			if (pullup(bpp,tmp,AXALEN) < AXALEN)
				break;
			memcpy(src.call,tmp,ALEN);
			src.ssid = tmp[ALEN];
			pax25(tmp,&src);
			fprintf(trfp,"        %-12s",tmp);
			pullup(bpp,tmp,ALEN);
			tmp[ALEN] = '\0';
			fprintf(trfp,"%8s",tmp);
			pullup(bpp,tmp,AXALEN);
			memcpy(src.call,tmp, ALEN);
			src.ssid = tmp[ALEN];
			pax25(tmp,&src);
			fprintf(trfp,"    %-12s", tmp);
			pullup(bpp,tmp,1);
			fprintf(trfp,"    %3u\n",uchar(tmp[0]));
		}
		return;
	}
	/* Decode network layer */
	pullup(bpp,tmp,AXALEN);
	memcpy(src.call,tmp,ALEN);
	src.ssid = tmp[ALEN];
	pax25(tmp,&src);
	fprintf(trfp,"NET/ROM n: %s",tmp);

	pullup(bpp,tmp,AXALEN);
	memcpy(dest.call,tmp,ALEN);
	dest.ssid = tmp[ALEN];
	pax25(tmp,&dest);
	fprintf(trfp,"->%s",tmp);

	pullup(bpp,&x,1);
	fprintf(trfp," ttl %d\n",uchar(x));

	nettra_dump(bpp);		/* dump transport layer */
}

/* Display NET/ROM transport header or IP datagram */
nettra_dump(bpp)
struct mbuf **bpp;
{
	struct ax25_addr src,dest;
	char x;
	char tmp[16];
	char thdr[5];
	struct mbuf *thbuf,*qdata();

	/* Read first five bytes of "transport" header */
	if (pullup(bpp,thdr,5) != 5)
		return;

	/* check if it is a net/rom transport layer packet */
	/* sorry, but this can only be guessed... */
	/* new encapsulation method uses NET/ROM transport opcode 0 */
	if ((thdr[0] & 0xf0) != (IPVERSION << 4) && (x = thdr[4] & 0xf) <= 6){
	    if (x == 0){	/* IP datagram (new encapsulation method) */
		ip_dump(bpp,1);
		return;
	    }

	    /* opcode 1..6, and first byte does not look like IP */
	    /* it most probably is a NET/ROM transport layer packet */
	    fprintf(trfp,"NET/ROM t: ");
	    switch(x){
	    case 1: /* Connect request */
		pullup(bpp,&x,1);
		fprintf(trfp,"conn rqst: ckt %d/%d wnd %d",
			     uchar(thdr[0]),uchar(thdr[1]),uchar(x));
		pullup(bpp,(char *)&src,AXALEN);
		pax25(tmp,&src);
		fprintf(trfp," %s",tmp);
		pullup(bpp,(char *)&dest,AXALEN);
		pax25(tmp,&dest);
		fprintf(trfp,"->%s",tmp);
		break;
	    case 2: /* Connect acknowledgement */
		pullup(bpp,&x,1);
		fprintf(trfp,"conn ack: ur ckt %d/%d my ckt %d/%d wnd %d",
			     uchar(thdr[0]),uchar(thdr[1]),uchar(thdr[2]),
			     uchar(thdr[3]),uchar(x));
		break;
	    case 3: /* Disconnect request */
		fprintf(trfp,"disc: ckt %d/%d",
			     uchar(thdr[0]),uchar(thdr[1]));
		break;
	    case 4: /* Disconnect acknowledgement */
		fprintf(trfp,"disc ack: ckt %d/%d",
			     uchar(thdr[0]),uchar(thdr[1]));
		break;
	    case 5: /* Information (data) */
		fprintf(trfp,"info: ckt %d/%d txseq %d rxseq %d",
			     uchar(thdr[0]),uchar(thdr[1]),
			     uchar(thdr[2]),uchar(thdr[3]));
		break;
	    case 6: /* Information acknowledgement */
		fprintf(trfp,"info ack: ckt %d/%d rxseq %d",
			     uchar(thdr[0]),uchar(thdr[1]),uchar(thdr[3]));
		break;
	    }
	    if(thdr[4] & 0x80)
		fprintf(trfp," CHOKE");
	    if(thdr[4] & 0x40)
		fprintf(trfp," NAK");
	    if(thdr[4] & 0x20)
		fprintf(trfp," MORE");
	    fprintf(trfp,"\n");
	} else {
	    /* maybe it's an IP datagram! (old encapsulation method) */
	    /* the following is gross and disgusting */
	    if((thbuf = qdata(thdr,5)) != NULLBUF){
		thbuf->next = *bpp;
		*bpp = thbuf;
		fprintf(trfp,"?");	/* indicate old method */
		ip_dump(bpp,1);
	    }
	}
}
char *
decode_type(type)
int16 type;
{
	switch(uchar(type)){
	case I:
		return "I";
	case RR:
		return "RR";
	case RNR:
		return "RNR";
	case REJ:
		return "REJ";
	case SREJ:
		return "SREJ";
	case UI:
		return "UI";
	case SIM:
		return "SIM";
	case DM:
		return "DM";
	case UP:
		return "UP";
	case SABM:
		return "SABM";
	case DISC:
		return "DISC";
	case SARME:
		return "SARME";
	case UA:
		return "UA";
	case SABME:
		return "SABME";
	case SNRM:
		return "SNRM";
	case XID:
		return "XID";
	case FRMR:
		return "FRMR";
	case RSET:
		return "RSET";
	case SNRME:
		return "SNRME";
	case TEST:
		return "TEST";
	default:
		return "[invalid]";
	}
}

