| /* |
| * IEEE 802.11. |
| */ |
| |
| #include <u.h> |
| #include <libc.h> |
| #include <ip.h> |
| #include "dat.h" |
| #include "protos.h" |
| |
| enum |
| { |
| Tmgmt = 0, |
| Tctl, |
| Tdata, |
| |
| CtlPoll = 0xA, |
| CtlRts, |
| CtlCts, |
| CtlAck, |
| CtlCfEnd, |
| CtlCfEndAck, |
| |
| Data = 0, |
| DataCfAck, |
| DataCfPoll, |
| DataCfAckPoll, |
| Nodata, |
| NodataCfAck, |
| NodataCfPoll, |
| NodataCfAckPoll, |
| |
| FlagTods = 0x1, |
| FlagFromds = 0x2, |
| FlagMoreflag = 0x4, |
| FlagRetry = 0x8, |
| FlagPowerMgmt = 0x10, |
| FlagMoreData = 0x20, |
| FlagWep = 0x40, |
| FlagOrder = 0x80, |
| |
| ProtoNone = 0, |
| ProtoLlc, |
| }; |
| |
| static Mux p_mux[] = |
| { |
| { "llc", ProtoLlc }, |
| { 0 } |
| }; |
| |
| typedef struct Hdr Hdr; |
| struct Hdr |
| { |
| uchar vers; |
| uchar type; |
| uchar subtype; |
| uchar flags; |
| ushort dur; |
| uchar aid; |
| uchar ra[6]; |
| uchar ta[6]; |
| uchar bssid[6]; |
| uchar sa[6]; |
| uchar da[6]; |
| ushort seq; |
| int proto; |
| int hdrlen; |
| }; |
| |
| static int |
| unpackhdr(uchar *p, uchar *ep, Hdr *h) |
| { |
| if(p+2 > ep) |
| return -1; |
| h->vers = p[0]&3; |
| if(h->vers != 0){ |
| h->hdrlen = 2; |
| return -1; |
| } |
| h->type = (p[0]>>2)&3; |
| h->subtype = (p[0]>>4)&15; |
| h->flags = p[1]; |
| h->hdrlen = 2; |
| |
| if(h->vers != 0) |
| return 0; |
| |
| switch(h->type){ |
| case Tmgmt: |
| // fc dur da sa bssid seq |
| if(p+2+2+6+6+6+2 > ep) |
| return -1; |
| h->hdrlen = 24; |
| h->dur = LittleS(p+2); |
| memmove(h->da, p+4, 6); |
| memmove(h->sa, p+10, 6); |
| memmove(h->bssid, p+16, 6); |
| h->seq = LittleS(p+22); |
| break; |
| |
| case Tctl: |
| switch(h->subtype){ |
| case CtlPoll: |
| // fc aid bssid ta |
| if(p+2+2+6+6 > ep) |
| return -1; |
| h->hdrlen = 16; |
| h->aid = LittleS(p+2); |
| memmove(h->bssid, p+4, 6); |
| memmove(h->ta, p+10, 6); |
| break; |
| |
| case CtlRts: |
| // fc dur ra ta |
| if(p+2+2+6+6 > ep) |
| return -1; |
| h->hdrlen = 16; |
| h->dur = LittleS(p+2); |
| memmove(h->ra, p+4, 6); |
| memmove(h->ta, p+10, 6); |
| break; |
| |
| case CtlCts: |
| case CtlAck: |
| // fc dur ra |
| if(p+2+2+6 > ep) |
| return -1; |
| h->hdrlen = 10; |
| h->dur = LittleS(p+2); |
| memmove(h->ra, p+4, 6); |
| break; |
| |
| case CtlCfEnd: |
| case CtlCfEndAck: |
| // fc dur ra bssid |
| if(p+2+2+6+6 > ep) |
| return -1; |
| h->hdrlen = 16; |
| h->dur = LittleS(p+2); |
| memmove(h->ra, p+4, 6); |
| memmove(h->bssid, p+10, 6); |
| break; |
| } |
| break; |
| |
| case Tdata: |
| if(p+24 > ep) |
| return -1; |
| h->hdrlen = 24; |
| h->dur = LittleS(p+2); // ??? maybe |
| // Also, what is at p+22? |
| |
| switch(h->flags&(FlagFromds|FlagTods)){ |
| case 0: |
| memmove(h->da, p+4, 6); |
| memmove(h->sa, p+10, 6); |
| memmove(h->bssid, p+16, 6); |
| break; |
| case FlagFromds: |
| memmove(h->da, p+4, 6); |
| memmove(h->bssid, p+10, 6); |
| memmove(h->sa, p+16, 6); |
| break; |
| case FlagTods: |
| memmove(h->bssid, p+4, 6); |
| memmove(h->sa, p+10, 6); |
| memmove(h->da, p+16, 6); |
| break; |
| case FlagFromds|FlagTods: |
| if(p+30 > ep) |
| return -1; |
| h->hdrlen = 30; |
| memmove(h->ra, p+4, 6); |
| memmove(h->ta, p+10, 6); |
| memmove(h->da, p+16, 6); |
| memmove(h->sa, p+24, 6); // 24 sic |
| break; |
| } |
| p += h->hdrlen; |
| h->proto = ProtoNone; |
| if(!(h->flags&FlagWep)) |
| h->proto = ProtoLlc; |
| break; |
| } |
| return 0; |
| } |
| |
| enum |
| { |
| Os, |
| Od, |
| Ot, |
| Or, |
| Obssid, |
| Oa, |
| Opr, |
| }; |
| |
| static Field p_fields[] = |
| { |
| { "s", Fether, Os, "source address" }, |
| { "d", Fether, Od, "destination address" }, |
| { "t", Fether, Ot, "transmit address" }, |
| { "r", Fether, Or, "receive address" }, |
| { "bssid", Fether, Obssid, "bssid address" }, |
| { "a", Fether, Oa, "any address" }, |
| { "sd", Fether, Oa, "source|destination address" }, |
| { 0 } |
| }; |
| |
| static void |
| p_compile(Filter *f) |
| { |
| Mux *m; |
| |
| if(f->op == '='){ |
| compile_cmp(p80211.name, f, p_fields); |
| return; |
| } |
| if(strcmp(f->s, "mgmt") == 0){ |
| f->pr = &p80211; |
| f->ulv = Tmgmt; |
| f->subop = Ot; |
| return; |
| } |
| if(strcmp(f->s, "ctl") == 0){ |
| f->pr = &p80211; |
| f->ulv = Tctl; |
| f->subop = Ot; |
| return; |
| } |
| if(strcmp(f->s, "data") == 0){ |
| f->pr = &p80211; |
| f->ulv = Tdata; |
| f->subop = Ot; |
| return; |
| } |
| 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 = Opr; |
| return; |
| } |
| } |
| sysfatal("unknown 802.11 field or protocol: %s", f->s); |
| } |
| |
| static int |
| p_filter(Filter *f, Msg *m) |
| { |
| Hdr h; |
| |
| memset(&h, 0, sizeof h); |
| if(unpackhdr(m->ps, m->pe, &h) < 0) |
| return 0; |
| m->ps += h.hdrlen; |
| |
| switch(f->subop){ |
| case Os: |
| return memcmp(h.sa, f->a, 6) == 0; |
| case Od: |
| return memcmp(h.da, f->a, 6) == 0; |
| case Ot: |
| return memcmp(h.ta, f->a, 6) == 0; |
| case Or: |
| return memcmp(h.ra, f->a, 6) == 0; |
| case Obssid: |
| return memcmp(h.bssid, f->a, 6) == 0; |
| case Oa: |
| return memcmp(h.sa, f->a, 6) == 0 |
| || memcmp(h.da, f->a, 6) == 0 |
| || memcmp(h.ta, f->a, 6) == 0 |
| || memcmp(h.ra, f->a, 6) == 0 |
| || memcmp(h.bssid, f->a, 6) == 0; |
| case Opr: |
| return h.proto == f->ulv; |
| } |
| return 0; |
| } |
| |
| static int |
| p_seprint(Msg *m) |
| { |
| Hdr h; |
| |
| memset(&h, 0, sizeof h); |
| if(unpackhdr(m->ps, m->pe, &h) < 0) |
| return -1; |
| |
| m->pr = &dump; |
| m->p = seprint(m->p, m->e, "fc=%02x flags=%02x ", m->ps[0], m->ps[1]); |
| switch(h.type){ |
| case Tmgmt: |
| m->p = seprint(m->p, m->e, "mgmt dur=%d d=%E s=%E bssid=%E seq=%d", |
| h.dur, h.da, h.sa, h.bssid, h.seq); |
| break; |
| case Tctl: |
| switch(h.subtype){ |
| case CtlPoll: |
| m->p = seprint(m->p, m->e, "ctl poll aid=%d bssid=%E t=%E", |
| h.aid, h.bssid, h.ta); |
| break; |
| case CtlRts: |
| m->p = seprint(m->p, m->e, "ctl rts dur=%d r=%E t=%E", |
| h.dur, h.ra, h.ta); |
| break; |
| case CtlCts: |
| m->p = seprint(m->p, m->e, "ctl cts dur=%d r=%E", |
| h.dur, h.ra); |
| break; |
| case CtlAck: |
| m->p = seprint(m->p, m->e, "ctl ack dur=%d r=%E", |
| h.dur, h.ra); |
| break; |
| case CtlCfEnd: |
| m->p = seprint(m->p, m->e, "ctl cf end dur=%d r=%E bssid=%E", |
| h.dur, h.ra, h.bssid); |
| break; |
| case CtlCfEndAck: |
| m->p = seprint(m->p, m->e, "ctl cf end ack dur=%d r=%E bssid=%E", |
| h.dur, h.ra, h.bssid); |
| break; |
| default: |
| m->p = seprint(m->p, m->e, "ctl %.*H", m->ps, h.hdrlen); |
| break; |
| } |
| break; |
| case Tdata: |
| switch(h.flags&(FlagFromds|FlagTods)){ |
| case 0: |
| m->p = seprint(m->p, m->e, "data d=%E s=%E bssid=%E", |
| h.da, h.sa, h.bssid); |
| break; |
| case FlagFromds: |
| m->p = seprint(m->p, m->e, "data fds d=%E bssid=%E s=%E", |
| h.da, h.bssid, h.sa); |
| break; |
| case FlagTods: |
| m->p = seprint(m->p, m->e, "data tds bssid=%E s=%E d=%E", |
| h.bssid, h.sa, h.da); |
| break; |
| case FlagFromds|FlagTods: |
| m->p = seprint(m->p, m->e, "data fds tds r=%E t=%E d=%E s=%E", |
| h.ra, h.ta, h.da, h.sa); |
| break; |
| } |
| if(!(h.flags&FlagWep)) |
| m->pr = &llc; |
| break; |
| } |
| m->ps += h.hdrlen; |
| return 0; |
| } |
| |
| Proto p80211 = |
| { |
| "802.11", |
| p_compile, |
| p_filter, |
| p_seprint, |
| p_mux, |
| nil, |
| nil, |
| defaultframer |
| }; |