Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/pppdec/pppdec.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/* pppdec.c - decode ppp packet dumps - Steve Simon, 2003 */

/*
 * reads log files which consist of three types of lines,
 * 1/	chatter			echoed
 * 2/	received data		eg: RX 02 43 4 23 43 f6 
 * 3/	transmitted data	eg: TX 34 54 23 54 65
 *
 * The parser is dumb, RX or TX must start the line,
 * there must be one space following it and then a list of
 * hex bytes seperated by single spaces.
 */

#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include "ppptables.h"

typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;

#define MTU	0xf000
#define INC(x)	((x) = (((x) + 1) % MTU))

typedef struct {
	char *name;
	uint head;
	uint tail;
	uchar buf[MTU];		/* circular buffer of raw data */

	int len;
	int pos;
	uchar pkt[MTU];		/* unescaped and frame checked data */

	int	count;	 	/* packet count - detects missing packets */
	int	resetid;	/* id of reset requests */
	int	indx;		/* current indx in history */
	int	size;		/* current history size */
	uchar	his[8096];	/* de-compression history buffer */
} pkt_t;

enum {
	IP_ICMP = 1,
	IP_TCP = 6,
	IP_UDP = 17,
	IP_more = 0x2000,
	IP_dont = 0x4000
};


pkt_t txpkt = { "send", 0, 0 };
pkt_t rxpkt = { "recv", 0, 0 };

char *argv0;
int Debug = 0;
int All = 0;

int dec_proto(pkt_t *);
int uncomp(pkt_t *);
int ipv4(pkt_t *);
int config(pkt_t *, tab_t *, tab_t *);
int options(pkt_t *, tab_t *, tab_t *);
int prval(char *, pkt_t *, tab_t *, int);
pkt_t *getbuf(Biobuf *);
pkt_t *readlog(Biobuf *);
uchar rdc(pkt_t *);
uchar rds(pkt_t *);
uchar rdl(pkt_t *);
int valid_fcs(uchar *, int);
tab_t *lookup(tab_t *, ushort);
int xdp(pkt_t *, int);
void xd(uchar *, int);
void usage(void);

void
main(int argc, char *argv[])
{
	int i;
	pkt_t *p;
	Biobuf bin, *bp;

	Binit(&bin, 0, OREAD);
	ARGBEGIN {
	case 'a':
		All++;
		break;
	case 'd':
		Debug++;
		break;
	default:
		usage();
	}ARGEND;

	for (i = 0; i < argc; i++){
		if ((bp = Bopen(argv[i], OREAD)) == nil){
			fprint(2, "%s: %s - cannot open file - %r\n", argv0, argv[1]);
			continue;
		}

		while ((p = getbuf(bp)) != nil){
			dec_proto(p);
			print("\n");
		}
	}

	if (argc == 0)
		while ((p = getbuf(&bin)) != nil){
			dec_proto(p);
			print("\n");
		}

	exits(0);
}

int
dec_proto(pkt_t *p)
{
	int rc = 0, proto;
	tab_t *tab;

	if (p->pkt[p->pos] == 0xff && p->pkt[p->pos +1] == 0x03) { /* uncompressed header */
		p->len -= 2;
		p->pos += 2;
	}

	proto = rdc(p);
	if (!(proto & 1))
		proto = (proto << 8) | rdc(p);

	if ((tab = lookup(Protocols, proto)) != nil)
		print("  proto='%s'\n", tab->str);
	else {
		print("#### unknown proto=0x%04x\n", proto);
		return -1;
	}

	switch (proto){
	case 0xc021:
		rc = config(p, Lcpopts, Lcpvals);
		break;
	case 0x80fd:
		rc = config(p, Ccpopts, Ccpvals);
		break;
	case 0x8021:
		rc = config(p, Ipcpopts, Ipcpvals);
		break;
	case 0x8053:
		rc = config(p, Ecpopts, nil);
		break;
	case 0x8049:
		rc = config(p, Sdcpopts, nil);
		break;
	case 0x00fd:
		rc = uncomp(p);
		break;
	case 0x0021:
		rc = ipv4(p);
		break;
	default:
		xdp(p, p->len);
		break;
	}

	if (rc == 0 && p->len)
		print(" padding nbytes=%d\n", p->len);
	return rc;
}



int
uncomp(pkt_t *p)
{
#define NEXTBYTE	sreg = (sreg << 8) | rdc(p); n--; bits += 8

	enum {
		Lit7,			       /* seven bit literal */
		Lit8,			       /* eight bit literal */
		Off6,			       /* six bit offset */
		Off8,			       /* eight bit offset */
		Off13,			       /* thirteen bit offset */
	};

	/* decode first four bits */
	int decode[16] = {
		Lit7,
		Lit7,
		Lit7,
		Lit7,
		Lit7,
		Lit7,
		Lit7,
		Lit7,
		Lit8,
		Lit8,
		Lit8,
		Lit8,
		Off13,
		Off13,
		Off8,
		Off6,
	};

	enum {
		Preset = (1 << 15),	       /* reset history */
		Pfront = (1 << 14),	       /* move packet to front of history */
		Pcompress = (1 << 13),	       /* packet is compressed */
		Pencrypt = (1 << 12),	       /* packet is encrypted */
	};

	int ecount, n, bits, off, len, ones;
	ushort count;
	ulong sreg;
	int t;
	uchar c, *hp, *hs, *he, *hq;

	count = rds(p);
	if (! (count & Pcompress)){
		print("    mppc='uncompressed'\n");
		return dec_proto(p);
	}

	print("    mppc='compressed' count=%d len=%d\n", count, p->len);

	if (count & Preset){
		p->indx = 0;
		p->size = 0;
	}
	else {
		ecount = (p->count + 1) & 0xfff;
		if ((count & 0xfff) != ecount)
			return -1;
		if (count & Pfront)
			p->indx = 0;
	}

	n = (((count + 1) >> 8) & 0xf) - (((p->count + 1)>> 8) & 0xf);

	bits = 0;
	sreg = 0;
	hs = p->his;			       /* history start */
	hp = hs + p->indx;		       /* write pointer in history */
	he = hs + sizeof(p->his);	       /* hsitory end */

	for (;;){
		if (bits < 4){
			if (n == 0)
				goto Done;
			NEXTBYTE;
		}
		t = decode[(sreg >> (bits - 4)) & 0xf];
		switch (t){
		default:
			print("#### mppc - bad decode!");
			return -1;
		case Lit7:
			bits -= 1;
			if (bits < 7){
				if (n == 0)
					goto Done;
				NEXTBYTE;
			}
			c = (sreg >> (bits - 7)) & 0x7f;
			bits -= 7;
			if (hp >= he)
				goto His;
			*hp++ = c;
			continue;
		case Lit8:
			bits -= 2;
			if (bits < 7){
				if (n == 0)
					goto Eof;
				NEXTBYTE;
			}
			c = 0x80 | ((sreg >> (bits - 7)) & 0x7f);
			bits -= 7;
			if (hp >= he)
				goto His;
			*hp++ = c;
			continue;
		case Off6:
			bits -= 4;
			if (bits < 6){
				if (n == 0)
					goto Eof;
				NEXTBYTE;
			}
			off = (sreg >> (bits - 6)) & 0x3f;
			bits -= 6;
			break;
		case Off8:
			bits -= 4;
			if (bits < 8){
				if (n == 0)
					goto Eof;
				NEXTBYTE;
			}
			off = ((sreg >> (bits - 8)) & 0xff) + 64;
			bits -= 8;
			break;
		case Off13:
			bits -= 3;
			while (bits < 13){
				if (n == 0)
					goto Eof;
				NEXTBYTE;
			}
			off = ((sreg >> (bits - 13)) & 0x1fff) + 320;
			bits -= 13;
			break;
		}
		for (ones = 0;; ones++){
			if (bits == 0){
				if (n == 0)
					goto Eof;
				NEXTBYTE;
			}
			bits--;
			if (!(sreg & (1 << bits)))
				break;
		}
		if (ones > 11){
			print("#### mppc - bad length %d\n", ones);
			return -1;
		}
		if (ones == 0){
			len = 3;
		}
		else {
			ones++;
			while (bits < ones){
				if (n == 0)
					goto Eof;
				NEXTBYTE;
			}
			len = (1 << ones) | ((sreg >> (bits - ones)) & ((1 << ones) - 1));
			bits -= ones;
		}

		hq = hp - off;
		if (hq < hs){
			hq += sizeof(p->his);
			if (hq - hs + len > p->size)
				goto His;
		}
		if (hp + len > he)
			goto His;
		while (len){
			*hp++ = *hq++;
			len--;
		}
	}
Done:

	/* build up return block */
	hq = hs + p->indx;
	len = hp - hq;


	p->indx += len;
	if (p->indx > p->size)
		p->size = p->indx;

	print("    mppc new len=%d\n", len);
	memmove(p->buf, hq, len);
	p->pos = 0;
	p->len = len;
	return dec_proto(p);
Eof:
	print("#### short packet\n");
	return -1;
His:
	print("#### bad history\n");
	return -1;
}

ushort
ipcsum(uchar *addr, int len)
{
	ulong sum = 0;

	while(len > 0){
		sum += (addr[0] << 8) | addr[1];
		len -= 2;
		addr += 2;
	}

	sum = (sum & 0xffff) + (sum >> 16);
	sum = (sum & 0xffff) + (sum >> 16);

	return sum ^ 0xffff;
}

int
ipv4(pkt_t *p)
{
	int n, hdrlen, pktlen, proto, olen;

/* not done yet, I don't need them..
	vj_dec(p);
	dump_ip(p);
*/

	olen = p->len;
	hdrlen = (p->pkt[p->pos] & 0x0f) << 2;
	pktlen = (p->pkt[p->pos + 2] << 8) | p->pkt[p->pos + 3];
	proto  = p->pkt[p->pos + 9];

	print("    IP hdrlen=%d pktlen=%d nbytes=%d\n", hdrlen, pktlen, p->len);

	if (ipcsum(p->pkt + p->pos, hdrlen) != 0){
		print("#### IP checksum fail\n");
		if (! All)
			return -1;
	}

	if (pktlen != p->len){
		print("#### IP bad payload len\n");
		if (! All)
			return -1;
	}


	n = rdc(p);
	print("    vers=%d\n", (n >> 4) & 0x0f);
	print("    hdrlen=%d\n", (n & 0x0f) << 2);

	prval("    tos=%bd\n", p, nil,0);
	prval("    pktlen=%d\n", p, nil,0);
	prval("    id=%d\n", p, nil,0);

	n = rds(p); 
	print("    fragoff=%ud %s %s\n",
		(n & 0x1fff),
		(n & IP_more)? "more": "",
		(n & IP_dont)? "don't": "");

	prval("    ttl=%bd\n", p, nil,0);
	prval("    proto=%bu\n", p, nil,0);
	prval("    check=0x%x\n", p, nil,0);
	prval("    src=%bd.%bd.%bd.%bd\n", p, nil,0);
	prval("    dest=%bd.%bd.%bd.%bd\n", p, nil,0);

	while (olen - p->len < hdrlen)		/* IP strip options */
		rdc(p);

	switch (proto){
	case IP_ICMP:
		print("      ICMP\n");
		prval("      type=%bd\n", p, nil, 0);
		prval("      code=%bd\n", p, nil, 0);
		break;
	case IP_TCP:
		print("      TCP\n");
		prval("      src port=%d\n", p, nil,0);
		prval("      dest port=%d\n", p, nil,0);
		prval("      seq=%lu\n", p, nil,0);
		prval("      ack=%lu\n", p, nil,0);
		prval("      flag=%x\n", p, nil,0);
		prval("      win=%ud\n", p, nil,0);
		prval("      check=0x%x\n", p, nil,0);
		prval("      urg=%ud\n", p, nil,0);
		break;
	case IP_UDP:
		print("      UDP\n");
		prval("      src port=%d\n", p, nil,0);
		prval("      dest port=%d\n", p, nil,0);
		prval("      len=%d\n", p, nil,0);
		prval("      chk=%d\n", p, nil,0);
		break;
	default:
		print("      proto=%d not supported\n", proto);
		break;
	}

	print("      datalen=%d\n", p->len);

	xdp(p, p->len);

	return 0;
}

int
config(pkt_t *p, tab_t *names, tab_t *values)
{
	tab_t *tab, *pro;
	int rc = 0, code, proto, magic, id, len;

	code = rdc(p);
	id = rdc(p);
	len = rds(p);
	len -= 4;

	if ((tab = lookup(Options, code)) != nil)
		print("   code='%s' id=%d len=%d\n", tab->str, id, len);
	else {
		print("#### unknown config code=%d id=%d len=%d\n", code, id, len);
		if (! All)
			return -1;
	}

	if (p->len < len){
		print("#### short packet\n");
		if (! All)
			return -1;
	}

	if (len == 0)
		return -1;			/* All done */

	switch (code){
	case 1:			       		/* Configure-Request */
	case 2:			       		/* Configure-Ack */
	case 3:			       		/* Configure-Nak */
	case 4:			       		/* Configure-Reject */
		rc = options(p, names, values);
		break;
	case 8:			       		/* Protocol-Reject */
		proto = rds(p);
		if ((pro = lookup(Protocols, proto)) != nil)
			print("    proto='%s'\n", pro->str);
		else{
			print("    unknown proto=%d\n", proto);
			rc = -1;
		}
		xdp(p, p->len);
		break;
	case 9:			       		/* Echo-Request */
	case 10:				/* Echo-Reply */
	case 11:				/* Discard-request */
		magic = rdl(p);
		print("    magic=0x%x\n", magic);
		xdp(p, p->len);
		break;
	default:
		xdp(p, p->len);
		break;
	}
	return rc;
}

int
options(pkt_t *p, tab_t * names, tab_t * values)
{
	tab_t *tab, *val;
	int rc = 0, type, len;

	do {

		type = rdc(p);
		len = rdc(p);
		len -= 2;

		if (len < 0)		/* probably zero padding */
			return 0;

		if (p->len < len){
			print("#### truncated packet\n");
			if (! All)
				return -1;
		}

		if ((tab = lookup(names, type)) != nil)
			print("     option type='%s' len=%d\n", tab->str, len);
		else {
			print("#### unknown option type=%d len=%d\n", type, len);
			xdp(p, len);
			continue;
		}

		if (values == nil){
			print("#### cannot decode value\n");
			xdp(p, len);
			continue;
		}

		if ((val = lookup(values, type)) != nil)
			prval(val->str, p, val->tab, len);
		else {
			print("### unknown value\n");
			xdp(p, len);
			continue;
		}

	} while (p->len);

	return rc;
}

int
prval(char *fmt, pkt_t *p, tab_t *tab, int len)
{
	long x;
	int i, w;
	tab_t *val;

	USED(len);

	for (; *fmt; fmt++){
		if (*fmt != '%'){
			print("%c", *fmt);
			continue;
		}
		w = 2;
again:

		switch (*++fmt){
		case 'b':
			w = 1;
			goto again;
		case 'w':
			w = 2;
			goto again;
		case 'l':
			w = 4;
			goto again;
		case 'u':
			for (x = i = 0; i < w; i++)
				x = (x << 8) | rdc(p);
			print("%lud", x);
			break;
		case 'd':
			for (x = i = 0; i < w; i++)
				x = (x << 8) | rdc(p);
			print("%ld", x);
			break;
		case 'x':
			for (x = i = 0; i < w; i++)
				x = (x << 8) | rdc(p);
			print("%lx", x);
			break;
		case 't':
			for (x = i = 0; i < w; i++)
				x = (x << 8) | rdc(p);

			if ((val = lookup(tab, x)) != nil)
				print("%s", val->str);
			else
				print("(unknown value=%ld)", x);
			break;
		case 's':
			for (i = 0; i < p->len; i++){
				uchar c = rdc(p);
				print("%c", (c < ' ' || c > '~')? '.': c);
			}
			break;
		default:
			print("%c", *fmt);
			break;
		}
	}
	return 0;
}

pkt_t *
getbuf(Biobuf *bp)
{
	uchar *b;
	int esc, eop;
	static pkt_t *p = &txpkt;

	do {

		for (; p->head != p->tail; INC(p->head)) 	/* find start of packet delimiter */
			if (p->buf[p->head] == '~')
				break;
		if (p->buf[p->head] != '~')
			continue;


		for (eop = p->head; eop != p->tail; INC(eop))	/* step over delimiter(s) */
			if (p->buf[eop] != '~')
				break;
		if (p->buf[eop] == '~')
			continue;

		for (; eop != p->tail; INC(eop))		/* find end of packet delimiter */
			if (p->buf[eop] == '~')
				break;
		if (p->buf[eop] != '~')
			continue;


		for (; p->head != p->tail; INC(p->head)) 	/* trim start of packet delimiter(s) */
			if (p->buf[p->head] != '~')
				break;
		if (p->head == eop)
			continue;

		print("%s\n", p->name);

		esc = 0;
		for (b = p->pkt; p->head != eop; INC(p->head)){
			if (p->buf[p->head] == '~')
				continue;

			if (p->buf[p->head] == '}')
				esc = 1;
			else
			if (esc){
				*b++ = p->buf[p->head] ^ 0x20;
				esc = 0;
			}
			else
				*b++ = p->buf[p->head];
		}

		if (Debug > 1){
			print(" pkt len=%ld\n", b - p->pkt);
			xd(p->pkt, b - p->pkt);
		}

		if (valid_fcs(p->pkt, (b - p->pkt))){
			p->pos = 0;
			p->len = (b - p->pkt) - 2;		/* -2 strips FCS */
			return p;

		}
		if (! All){
			p->pos = 0;
			p->len = (b - p->pkt);
			return p;
		}

	} while ((p = readlog(bp)) != nil);

	return nil;		/* EOF */
}

pkt_t *
readlog(Biobuf *bp)
{
	uchar *b;
	int i;
	char *s, *str;
	uchar tmp[MTU];
	pkt_t *p;

again:
	while ((str = Brdline(bp, '\n')) == nil)
		return nil;
	str[Blinelen(bp) -1] = 0;

	if (Debug)
		print("\n> %s", str);

	if (strncmp("RX", str, 2) == 0)
		p = &rxpkt;
	else
	if (strncmp("TX", str, 2) == 0)
		p = &txpkt;
	else
		goto again;

	for (s = str + 3; isxdigit(*s); INC(p->tail)){
		p->buf[p->tail] = (uchar)strtoul(s, &s, 16);
		while (*s == ' ')
			s++;
	}

	if (Debug > 1){

		print(" buf len=%d\n", p->tail - p->head);
		for (i = p->head, b = tmp; i != p->tail; INC(i))
			*b++ = p->buf[i];
		xd(tmp, b - tmp);
	}
	return p;
}

uchar
rdc(pkt_t *p)
{
	if (--p->len < 0){
		print("short packet\n");
		return 0;
	}

	return p->pkt[p->pos++];
}

uchar
rds(pkt_t *p)
{
	ushort x;

	p->len -= 2;
	if (p->len < 0){
		print("short packet\n");
		return 0;
	}

	x = p->pkt[p->pos++];
	x = (x << 8) | p->pkt[p->pos++];
	return x;
}

uchar
rdl(pkt_t *p)
{
	ulong x;

	p->len -= 4;
	if (p->len < 0){
		print("short packet\n");
		return 0;
	}

	x = p->pkt[p->pos++];
	x = (x << 8) | p->pkt[p->pos++];
	x = (x << 8) | p->pkt[p->pos++];
	x = (x << 8) | p->pkt[p->pos++];
	return x;
}

int
valid_fcs(uchar * buf, int n)
{
	int i;
	ushort fcs = 0xffff;

	for (i = 0; i < n; i++){
		fcs = (fcs >> 8) ^ fcstab[(fcs ^ buf[i]) & 0xff];
	}

	if (fcs == 0xf0b8)
		return 1;
	print("#### fcs fail\n");
	return 0;
}

tab_t *
lookup(tab_t * p, ushort n)
{
	for (; p->n != -1; p++)
		if (p->n == n)
			return p;

	return nil;
}

int
xdp(pkt_t *p, int len)
{
	if (p->len < len){
		print("### short packet\n");
		p->pos += p->len;
		p->len = 0;
		return -1;
	}

	xd(p->pkt + p->pos, len);
	p->len -= len;
	p->pos += len;
	return 0;
}

void
xd(uchar *buf, int len)
{
	int i, j, addr;

	for (addr = 0; addr < len; addr += 16, buf += 16){

		print("	%04x  ", addr);

		for (i = 0; i < 16 && i < (len - addr); i++)
			print("%02x ", buf[i]);

		for (j = i; j < 16; j++)
			print("   ");

		print("  ");

		for (i = 0; i < 16 && i < (len - addr); i++){
			uchar c = buf[i];
			print("%c", (c < ' ' || c > '~')? '.': c);
		}

		print("\n");
	}
}

void
usage()
{
	fprint(2, "usage: %s [-db] <file...>\n", argv0);
	fprint(2, "-d	increase debugging chatter\n");
	fprint(2, "-a	attempt decode packets which look invalid\n");
	exits("usage");
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].