/* 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");
}
|