/* 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 #include #include #include #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] \n", argv0); fprint(2, "-d increase debugging chatter\n"); fprint(2, "-a attempt decode packets which look invalid\n"); exits("usage"); }