/* @(#) $Header: ax25dump.c,v 1.5 91/02/24 20:16:33 deyke Exp $ */

/* AX25 header tracing
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#include "global.h"

#define	LAPB_UNKNOWN	0
#define	LAPB_COMMAND	1
#define	LAPB_RESPONSE	2

#define	SEG_FIRST	0x80
#define	SEG_REM		0x7F

#define	PID_SEGMENT	0x08
#define	PID_ARP		0xCD
#define	PID_NETROM	0xCF
#define	PID_IP		0xCC
#define	PID_X25		0x01
#define	PID_TEXNET	0xC3
#define	PID_FLEXNET	0xCE
#define	PID_NO_L3	0xF0

#define	I		0x00
#define	S		0x01
#define	RR		0x01
#define	RNR		0x05
#define	REJ		0x09
#define	U		0x03
#define	SABM		0x2F
#define	DISC		0x43
#define	DM		0x0F
#define	UA		0x63
#define	FRMR		0x87
#define	UI		0x03
#define	PF		0x10

#define	MMASK		7

#define	HDLCAEB		0x01
#define	SSID		0x1E
#define	REPEATED	0x80
#define	C		0x80

#define	ALEN		6
#define	AXLEN		7

#define	W		1
#define	X		2
#define	Y		4
#define	Z		8

static int  ftype(int);
static char *decode_type(int);

/* Dump an AX.25 packet header */
void ax25_dump(unsigned char *data, int length)
{
	char tmp[15];
	int control, pid, seg, type, end, cmdrsp;

	fprintf(stdout,"AX25: ");
	/* Extract the address header */
	if (length < (AXLEN + AXLEN + 1))
	{
		/* Something wrong with the header */
		fprintf(stdout, " bad header!\n");
		return;
	}
	
	fprintf(stdout, "%s", pax25(tmp, data + AXLEN));
	fprintf(stdout, "->%s", pax25(tmp, data));

	cmdrsp = LAPB_UNKNOWN;

	if ((data[ALEN] & C) && !(data[AXLEN + ALEN] & C))
		cmdrsp = LAPB_COMMAND;

	if ((data[AXLEN + ALEN] & C) && !(data[ALEN] & C))
		cmdrsp = LAPB_RESPONSE;

	end = (data[AXLEN + ALEN] & HDLCAEB);

	data   += (AXLEN + AXLEN);
	length -= (AXLEN + AXLEN);

	if (!end)
	{
		fprintf(stdout, " v");

		while (!end)
		{
			/* Print digi string */
			fprintf(stdout," %s%s", pax25(tmp, data),
			 (data[ALEN] & REPEATED) ? "*" : "");
			
			end = (data[ALEN] & HDLCAEB);
			
			data += AXLEN;
			length -= AXLEN;
		}
	}
	
	if (length == 0) return;
	
	control = *data++;
	length--;
	
	putc(' ', stdout);
	type = ftype(control);
	
	fprintf(stdout, "<%s", decode_type(type));

	switch (cmdrsp)
	{
		case LAPB_COMMAND:
			fprintf(stdout, " C");
			if (control & PF) fprintf(stdout, " P");
			break;
		case LAPB_RESPONSE:
			fprintf(stdout, " R");
			if (control & PF) fprintf(stdout, " F");
			break;
		default:
			break;
	}

	if ((type & 0x3) != U)   /* I or S frame? */
		fprintf(stdout, " R%d", (control >> 5) & 7);
	if (type == I || type == UI)
	{
		if (type == I) fprintf(stdout, " S%d", (control >> 1) & 7);
		fputc('>', stdout);
		/* Decode I field */
		if (length > 0)
		{        /* Get pid */
			pid = *data++;
			length--;
		
			if (pid == PID_SEGMENT)
			{
				seg = *data++;
				length--;
				fprintf(stdout, "%s remain %u", seg & SEG_FIRST ?
				 " First seg;" : "", seg & SEG_REM);
				if (seg & SEG_FIRST)
				{
					pid = *data++;
					length--;
				}
			}

			switch(pid)
			{
			case PID_SEGMENT:
				putc('\n', stdout);
				break;  /* Already displayed */
			case PID_ARP:
				fprintf(stdout," pid=ARP\n");
				arp_dump(data, length);
				break;
			case PID_NETROM:
				fprintf(stdout," pid=NET/ROM\n");
				netrom_dump(data, length);
				break;
			case PID_IP:
				fprintf(stdout," pid=IP\n");
				ip_dump(data, length);
				break;
			case PID_X25:
				fprintf(stdout, " pid=X.25\n");
				data_dump(data, length);
				break;
			case PID_TEXNET:
				fprintf(stdout, " pid=TEXNET\n");
				data_dump(data, length);
				break;
			case PID_FLEXNET:
				fprintf(stdout, " pid=FLEXNET\n");
				data_dump(data, length);
				break;
			case PID_NO_L3:
				fprintf(stdout, " pid=Text\n");
				data_dump(data, length);
				break;
			default:
				fprintf(stdout, " pid=0x%x\n", pid);
				data_dump(data, length);
				break;
			}
		}
	} else if (type == FRMR && length >= 3){
		fprintf(stdout, ": %s", decode_type(ftype(data[0])));
		fprintf(stdout, " Vr = %d Vs = %d",
			(data[1] >> 5) & MMASK,
			(data[1] >> 1) & MMASK);
		if(data[2] & W)
			fprintf(stdout, " Invalid control field");
		if(data[2] & X)
			fprintf(stdout, " Illegal I-field");
		if(data[2] & Y)
			fprintf(stdout, " Too-long I-field");
		if(data[2] & Z)
			fprintf(stdout, " Invalid seq number");
		putc('\n',stdout);
	} else
		fprintf(stdout, ">\n");

}

static char *decode_type(int type)
{
	switch (type)
	{
	case I:
		return "I";
	case SABM:
		return "C";
	case DISC:
		return "D";
	case DM:
		return "DM";
	case UA:
		return "UA";
	case RR:
		return "RR";
	case RNR:
		return "RNR";
	case REJ:
		return "REJ";
	case FRMR:
		return "FRMR";
	case UI:
		return "UI";
	default:
		return "[invalid]";
	}
}

char *pax25(char *buf, unsigned char *data)
{
	int i, ssid;
	char *s;
	char c;
	
	s = buf;
	
	for (i = 0; i < ALEN; i++)
	{
		c = (data[i] >> 1) & 0x7F;
		
		if (c != ' ') *s++ = c;
	}	

	if ((ssid = (data[ALEN] & SSID)) != 0)
		sprintf(s, "-%d", ssid >> 1);
	else
		*s = '\0';

	return(buf);
}

static int ftype(int control)
{
	if ((control & 1) == 0)	/* An I frame is an I-frame ... */
		return I;
	if (control & 2)		/* U-frames use all except P/F bit for type */
		return (control & ~PF);
	else				/* S-frames use low order 4 bits for type */
		return (control & 0x0F);
}
