Add support for eapol, eap, ttls and rc4 key descriptors. These things are used by 802.1X. Though I may still tweak this a bit more, I think it should already be useful to others and more or less ready for prime time. Some possible critisism on this version: Maybe some of the sub-protocol names and field names I use are a bit (too?) verbose. The naming of fields is not entirely uniform. The capitalization of the subprotocols as printed in the `encapsulating' header might be a bit too much (though dhcp/bootp capitalize too). Right now I also `dump' the contents of ttls packets, which may be superflous. I don't know yet what I prefer. I'm not convinced I got the support for filtering etc. exactly right (i.e. all fields of Proto) but what I have seems to work for me. Nevertheless, I'm submitting this anyway because I think it is already useful as-is, even when improvement is possible. Axel. Reference: /n/sources/patch/applied/snoopy-eapol-eap-ttls-rc4keydesc Date: Mon Sep 26 13:52:32 CES 2005 --- /sys/src/cmd/ip/snoopy/ether.c Mon Sep 26 13:34:32 2005 +++ /sys/src/cmd/ip/snoopy/ether.c Mon Sep 26 13:34:30 2005 @@ -23,6 +23,7 @@ {"ip6", 0x86dd, } , {"pppoe_disc", 0x8863, }, {"pppoe_sess", 0x8864, }, + {"eapol", 0x888e, }, {0} }; --- /sys/src/cmd/ip/snoopy/eapol.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ip/snoopy/eapol.c Mon Sep 26 13:34:42 2005 @@ -0,0 +1,137 @@ +#include +#include +#include +#include "dat.h" +#include "protos.h" + +typedef struct Hdr Hdr; +struct Hdr +{ + uchar vi; /* version */ + uchar type; + uchar len[2]; /* length of data following this header */ +}; + +enum +{ + EAPOLHDR= 4, /* sizeof(Hdr) */ + + /* eapol types */ + Eap = 0, + Start, + Logoff, + Key, + AsfAlert, +}; + +enum +{ + Ot, /* type */ +}; + +static Mux p_mux[] = +{ + { "eap", Eap, }, + { "eapol_start", Start, }, + { "eapol_logoff", Logoff, }, + { "eapol_key", Key, }, + { "asf_alert", AsfAlert, }, + { 0 } +}; + +static void +p_compile(Filter *f) +{ + Mux *m; + + for(m = p_mux; m->name != nil; m++) + if(strcmp(f->s, m->name) == 0){ + f->pr = m->pr; + f->ulv = m->val; + f->subop = Ot; + return; + } + sysfatal("unknown eapol field or type: %s", f->s); +} + +static int +p_filter(Filter *f, Msg *m) +{ + Hdr *h; + + if(m->pe - m->ps < EAPOLHDR) + return 0; + + h = (Hdr*)m->ps; + + /* len does not include header */ + m->ps += EAPOLHDR; + + switch(f->subop){ + case Ot: + return h->type == f->ulv; + } + return 0; +} + +static char* +op(int i) +{ + static char x[20]; + + switch(i){ + case Eap: + return "Eap"; + case Start: + return "Start"; + case Logoff: + return "Logoff"; + case Key: + return "Key"; + case AsfAlert: + return "AsfAlert"; + default: + sprint(x, "%1d", i); + return x; + } +} + +static int +p_seprint(Msg *m) +{ + Hdr *h; + int len; + + if(m->pe - m->ps < EAPOLHDR) + return -1; + + h = (Hdr*)m->ps; + + /* len does not include header */ + m->ps += EAPOLHDR; + + /* truncate the message if there's extra */ + len = NetS(h->len); + if(m->ps + len < m->pe) + m->pe = m->ps + len; + else if(m->ps+len > m->pe) + return -1; + + /* next protocol depending on type*/ + demux(p_mux, h->type, h->type, m, &dump); + + m->p = seprint(m->p, m->e, "type=%s version=%1d datalen=%1d", + op(h->type), h->vi, len); + return 0; +} + +Proto eapol = +{ + "eapol", + p_compile, + p_filter, + p_seprint, + p_mux, + nil, + defaultframer, +}; --- /sys/src/cmd/ip/snoopy/eapol_key.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ip/snoopy/eapol_key.c Mon Sep 26 13:34:52 2005 @@ -0,0 +1,153 @@ +#include +#include +#include +#include "dat.h" +#include "protos.h" + +typedef struct Hdr +{ + uchar desc; +} Hdr; + +typedef struct Rc4KeyDesc +{ + uchar ln[2]; + uchar replay[8]; + uchar iv[16]; + uchar idx; + uchar md[16]; +} Rc4KeyDesc; + +enum +{ + HDR= 1, /* sizeof(Hdr) */ + RC4KEYDESC= 43, /* sizeof(Rc4KeyDesc) */ + + DescTpRC4= 1, +}; + +enum +{ + Odesc, +}; + +static Mux p_mux[] = +{ + { "rc4keydesc", DescTpRC4, }, + { 0 } +}; + +static Mux p_muxrc4[] = +{ + { "dump", 0, }, + { 0 } +}; + +static void +p_compile(Filter *f) +{ + Mux *m; + + for(m = p_mux; m->name != nil; m++) + if(strcmp(f->s, m->name) == 0){ + f->pr = m->pr; + f->ulv = m->val; + f->subop = Odesc; + return; + } + sysfatal("unknown eap_key field or type: %s", f->s); +} + +static int +p_filter(Filter *f, Msg *m) +{ + Hdr *h; + + if(m->pe - m->ps < HDR) + return 0; + + h = (Hdr*)m->ps; + m->ps += HDR; + + switch(f->subop){ + case Odesc: + return h->desc == f->ulv; + } + return 0; +} + +static char* +op(int i) +{ + static char x[20]; + + switch(i){ + case DescTpRC4: + return "RC4KeyDesc"; + default: + sprint(x, "%1d", i); + return x; + } +} + +static int +p_seprint(Msg *m) +{ + Hdr *h; + + if(m->pe - m->ps < HDR) + return -1; + + h = (Hdr*)m->ps; + m->ps += HDR; + + /* next protocol depending on type*/ + demux(p_mux, h->desc, h->desc, m, &dump); + + m->p = seprint(m->p, m->e, "desc=%s", op(h->desc)); + return 0; +} + +static int +p_seprintrc4(Msg *m) +{ + Rc4KeyDesc *h; + int len; + + if(m->pe - m->ps < RC4KEYDESC) + return -1; + + h = (Rc4KeyDesc*)m->ps; + m->ps += RC4KEYDESC; + m->pr = nil; + len = m->pe - m->ps; + + m->p = seprint(m->p, m->e, "keylen=%1d replay=%1d iv=%1d idx=%1d md=%1d", + NetS(h->ln), NetS(h->replay), NetS(h->iv), h->idx, NetS(h->md)); + m->p = seprint(m->p, m->e, " dataln=%d", len); + if (len > 0) + m->p = seprint(m->p, m->e, " data=%.*H", len, m->ps); + return 0; +} + +Proto eapol_key = +{ + "eapol_key", + p_compile, + p_filter, + p_seprint, + p_mux, + nil, + defaultframer, +}; + +Proto rc4keydesc = +{ + "rc4keydesc", + p_compile, + nil, + p_seprintrc4, + nil, + nil, + defaultframer, +}; --- /sys/src/cmd/ip/snoopy/eap.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ip/snoopy/eap.c Mon Sep 26 14:29:52 2005 @@ -0,0 +1,245 @@ +#include +#include +#include +#include "dat.h" +#include "protos.h" + +typedef struct Hdr Hdr; +struct Hdr +{ + uchar code; + uchar id; + uchar len[2]; /* length including this header */ + + uchar tp; /* optional, only for Request/Response */ +}; + +enum +{ + EAPHDR= 4, /* sizeof(code)+sizeof(id)+sizeof(len) */ + TPHDR= 1, /* sizeof(tp) */ + + /* eap types */ + Request = 1, + Response, + Success, + Fail, + + /* eap request/response sub-types */ + Identity = 1, /* Identity */ + Notify, /* Notification */ + Nak, /* Nak (Response only) */ + Md5, /* MD5-challenge */ + Otp, /* one time password */ + Gtc, /* generic token card */ + Ttls = 21, /* tunneled TLS */ + Xpnd = 254, /* expanded types */ + Xprm, /* experimental use */ +}; + +enum +{ + Ot, +}; + +static Mux p_mux[] = +{ + { "eap_identity", Identity, }, + { "eap_notify", Notify, }, + { "eap_nak", Nak, }, + { "eap_md5", Md5, }, + { "eap_otp", Otp, }, + { "eap_gtc", Gtc, }, + { "ttls", Ttls, }, + { "eap_xpnd", Xpnd, }, + { "eap_xprm", Xprm, }, + { 0 } +}; + +static char *eapsubtype[256] = +{ +[Identity] "Identity", +[Notify] "Notify", +[Nak] "Nak", +[Md5] "Md5", +[Otp] "Otp", +[Gtc] "Gtc", +[Ttls] "Ttls", +[Xpnd] "Xpnd", +[Xprm] "Xprm", +}; + + +static void +p_compile(Filter *f) +{ + Mux *m; + + for(m = p_mux; m->name != nil; m++) + if(strcmp(f->s, m->name) == 0){ + f->pr = m->pr; + f->ulv = m->val; + f->subop = Ot; + return; + } + sysfatal("unknown eap field or type: %s", f->s); +} + +static int +p_filter(Filter *f, Msg *m) +{ + Hdr *h; + int len; + + if(f->subop != Ot) + return 0; + + if(m->pe - m->ps < EAPHDR) + return -1; + + h = (Hdr*)m->ps; + + /* truncate the message if there's extra */ + /* len includes header */ + len = NetS(h->len); + if(m->ps+len < m->pe) + m->pe = m->ps+len; + else if(m->ps+len > m->pe) + return -1; + m->ps += EAPHDR; + + if(h->code != Request && h->code != Response) + return 0; + m->ps += TPHDR; + + if(h->tp == f->ulv) + return 1; + + return 0; +} + +static char* +op(int i) +{ + static char x[20]; + + switch(i){ + case Request: + return "Request"; + case Response: + return "Response"; + case Success: + return "Success"; + case Fail: + return "Fail"; + default: + sprint(x, "%1d", i); + return x; + } +} + +static char* +subop(uchar val) +{ + static char x[20], *p; + + p = eapsubtype[val]; + if(p != nil) + return p; + else { + sprint(x, "%1d", val); + return x; + } +} + +static int +p_seprint(Msg *m) +{ + Hdr *h; + int len; + char *p, *e; + + if(m->pe - m->ps < EAPHDR) + return -1; + + p = m->p; + e = m->e; + h = (Hdr*)m->ps; + + /* resize packet (should already be done by eapol) */ + /* len includes header */ + len = NetS(h->len); + if(m->ps+len < m->pe) + m->pe = m->ps+len; + else if(m->ps+len > m->pe) + return -1; + m->ps += EAPHDR; + + p = seprint(p, e, "id=%1d code=%s", h->id, op(h->code)); + switch(h->code) { + case Request: + case Response: + m->ps += TPHDR; + p = seprint(p, e, " type=%s", subop(h->tp)); + /* special case needed to print eap_notify notification as unicode */ + demux(p_mux, h->tp, h->tp, m, &dump); + break; + default: + demux(p_mux, 0, 0, m, &dump); + break; + } + m->p = seprint(p, e, " len=%1d", len); + return 0; +} + +static int +p_seprintidentity(Msg *m) +{ + char *ps, *pe, *z; + int len; + + m->pr = nil; + ps = (char*)m->ps; + pe = (char*)m->pe; + + /* we would like to do this depending on the 'context': + * - one for eap_identity request and + * - one for eap_identity response + * but we've lost the context, or haven't we? + * so we treat them the same, so we might erroneously + * print a response as if it was a request. too bad. - axel + */ + for (z=ps; *z != '\0' && z+1 < pe; z++) + ; + if (*z == '\0' && z+1 < pe) { + m->p = seprint(m->p, m->e, "prompt=(%s)", ps); + len = pe - (z+1); + m->p = seprint(m->p, m->e, " options=(%.*s)", len, z+1); + } else { + len = pe - ps; + m->p = seprint(m->p, m->e, "%.*s", len, ps); + } + return 0; +} + +Proto eap = +{ + "eap", + p_compile, + p_filter, + p_seprint, + p_mux, + nil, + defaultframer, +}; + +Proto eap_identity = +{ + "eap_identity", + p_compile, + p_filter, + p_seprintidentity, + nil, + nil, + defaultframer, +}; --- /sys/src/cmd/ip/snoopy/eap_identity.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ip/snoopy/eap_identity.c Mon Sep 26 13:35:18 2005 @@ -0,0 +1 @@ +/* place holder, this stuff is really in eap.c */ --- /sys/src/cmd/ip/snoopy/rc4keydesc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ip/snoopy/rc4keydesc.c Mon Sep 26 13:35:32 2005 @@ -0,0 +1 @@ +/* place holder, this stuff is really in eapol_key.c */ --- /sys/src/cmd/ip/snoopy/ttls.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ip/snoopy/ttls.c Mon Sep 26 13:35:47 2005 @@ -0,0 +1,95 @@ +#include +#include +#include +#include "dat.h" +#include "protos.h" + +typedef struct Hdr Hdr; +struct Hdr +{ + uchar flags; + uchar ln[4]; /* optional, present if L flag set*/ +}; + +enum +{ + FLHDR= 1, /* sizeof(flags) */ + LNHDR= 4, /* sizeof(ln) */ +}; + +enum +{ + FlagL = 1<<7, + FlagM = 1<<6, + FlagS = 1<<5, + Version = (1<<2)|(1<<1)|(1<<0), +}; + +static Mux p_mux[] = +{ + { "dump", 0, }, + { 0 } +}; + +static char* +flags(int f) +{ + static char fl[20]; + char *p; + + p = fl; + if(f & FlagS) + *p++ = 'S'; + if(f & FlagM) + *p++ = 'M'; + if(f & FlagL) + *p++ = 'L'; + *p = 0; + return fl; +} + +static int +p_seprint(Msg *m) +{ + Hdr *h; + + if(m->pe - m->ps < FLHDR) + return -1; + + h = (Hdr*)m->ps; + m->ps += FLHDR; + + if (h->flags & FlagL) { + if(m->pe - m->ps < LNHDR) + return -1; + else + m->ps += LNHDR; + } + + /* next protocol depending on type*/ + demux(p_mux, 0, 0, m, &dump); + + m->p = seprint(m->p, m->e, "ver=%1d", h->flags & Version); + m->p = seprint(m->p, m->e, " fl=%s", flags(h->flags)); + + if (h->flags & FlagL) + m->p = seprint(m->p, m->e, " totallen=%1d", NetL(h->ln)); + + /* these are not in the header, just print them for our convenience */ + m->p = seprint(m->p, m->e, " dataln=%1ld", m->pe - m->ps); + if ((h->flags & (FlagL|FlagS|FlagM)) == 0 && m->ps == m->pe) + m->p = seprint(m->p, m->e, " ack"); + + return 0; +} + +Proto ttls = +{ + "ttls", + nil, + nil, + p_seprint, + p_mux, /* we need this to get the dump printed */ + nil, + defaultframer, +}; --- /sys/src/cmd/ip/snoopy/mkfile Mon Sep 26 13:36:05 2005 +++ /sys/src/cmd/ip/snoopy/mkfile Mon Sep 26 13:36:03 2005 @@ -27,6 +27,12 @@ ppp_ipcp\ pppoe_sess\ pppoe_disc\ + eapol\ + eapol_key\ + rc4keydesc\ + eap\ + eap_identity\ + ttls POBJS=${PROTOS:%=%.$O}